files_transaction_commit(): clean up empty directories

When deleting/pruning references, remove any directories that are made empty by the deletion of loose references or of reflogs. Otherwise such empty directories can survive forever and accumulate over time. (Even 'pack-refs', which is smart enough to remove the parent directories of loose references that it prunes, leaves directories that were already empty.) And now that files_transaction_commit() takes care of deleting the parent directories of loose references that it prunes, we don't have to do that in prune_ref() anymore. This change would be unwise if the *creation* of these directories could race with our deletion of them. But the earlier changes in this patch series made the creation paths robust against races, so now it is safe to tidy them up more aggressively. Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Reviewed-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Haggerty committed Jan 6, 2017 at 17:22 UTC 446397774ad3b9f2ae81bcfbe0628e99c3db7e49
3 files changed +64 -8
refs/files-backend.c
+28 -6
@@ -2346,7 +2346,6 @@ static void prune_ref(struct ref_to_prune *r)
2346 }
2347 ref_transaction_free(transaction);
2348 strbuf_release(&err);
2349 - try_remove_empty_parents(r->name, REMOVE_EMPTY_PARENTS_REF);
2349 }
2350
2351 static void prune_refs(struct ref_to_prune *r)
@@ -3794,6 +3793,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
3793 ret = TRANSACTION_GENERIC_ERROR;
3794 goto cleanup;
3795 }
3796 + update->flags |= REF_DELETED_LOOSE;
3797 }
3798
3799 if (!(update->flags & REF_ISPRUNING))
@@ -3806,16 +3806,38 @@ static int files_transaction_commit(struct ref_store *ref_store,
3806 ret = TRANSACTION_GENERIC_ERROR;
3807 goto cleanup;
3808 }
3809 - for_each_string_list_item(ref_to_delete, &refs_to_delete)
3810 - unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
3809 +
3810 + /* Delete the reflogs of any references that were deleted: */
3811 + for_each_string_list_item(ref_to_delete, &refs_to_delete) {
3812 + if (!unlink_or_warn(git_path("logs/%s", ref_to_delete->string)))
3813 + try_remove_empty_parents(ref_to_delete->string,
3814 + REMOVE_EMPTY_PARENTS_REFLOG);
3815 + }
3816 +
3817 clear_loose_ref_cache(refs);
3818
3819 cleanup:
3820 transaction->state = REF_TRANSACTION_CLOSED;
3821
3816 - for (i = 0; i < transaction->nr; i++)
3817 - if (transaction->updates[i]->backend_data)
3818 - unlock_ref(transaction->updates[i]->backend_data);
3822 + for (i = 0; i < transaction->nr; i++) {
3823 + struct ref_update *update = transaction->updates[i];
3824 + struct ref_lock *lock = update->backend_data;
3825 +
3826 + if (lock)
3827 + unlock_ref(lock);
3828 +
3829 + if (update->flags & REF_DELETED_LOOSE) {
3830 + /*
3831 + * The loose reference was deleted. Delete any
3832 + * empty parent directories. (Note that this
3833 + * can only work because we have already
3834 + * removed the lockfile.)
3835 + */
3836 + try_remove_empty_parents(update->refname,
3837 + REMOVE_EMPTY_PARENTS_REF);
3838 + }
3839 + }
3840 +
3841 string_list_clear(&refs_to_delete, 0);
3842 free(head_ref);
3843 string_list_clear(&affected_refnames, 0);
refs/refs-internal.h
+9 -2
@@ -55,6 +55,12 @@
55 */
56 #define REF_UPDATE_VIA_HEAD 0x100
57
58 +/*
59 + * Used as a flag in ref_update::flags when the loose reference has
60 + * been deleted.
61 + */
62 +#define REF_DELETED_LOOSE 0x200
63 +
64 /*
65 * Return true iff refname is minimally safe. "Safe" here means that
66 * deleting a loose reference by this name will not do any damage, for
@@ -158,8 +164,9 @@ struct ref_update {
164
165 /*
166 * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
161 - * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY, and
162 - * REF_UPDATE_VIA_HEAD:
167 + * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY,
168 + * REF_UPDATE_VIA_HEAD, REF_NEEDS_COMMIT, and
169 + * REF_DELETED_LOOSE:
170 */
171 unsigned int flags;
172
t/t1400-update-ref.sh
+27
@@ -191,6 +191,33 @@ test_expect_success \
191 git update-ref HEAD'" $A &&
192 test $A"' = $(cat .git/'"$m"')'
193
194 +test_expect_success "empty directory removal" '
195 + git branch d1/d2/r1 HEAD &&
196 + git branch d1/r2 HEAD &&
197 + test -f .git/refs/heads/d1/d2/r1 &&
198 + test -f .git/logs/refs/heads/d1/d2/r1 &&
199 + git branch -d d1/d2/r1 &&
200 + ! test -e .git/refs/heads/d1/d2 &&
201 + ! test -e .git/logs/refs/heads/d1/d2 &&
202 + test -f .git/refs/heads/d1/r2 &&
203 + test -f .git/logs/refs/heads/d1/r2
204 +'
205 +
206 +test_expect_success "symref empty directory removal" '
207 + git branch e1/e2/r1 HEAD &&
208 + git branch e1/r2 HEAD &&
209 + git checkout e1/e2/r1 &&
210 + test_when_finished "git checkout master" &&
211 + test -f .git/refs/heads/e1/e2/r1 &&
212 + test -f .git/logs/refs/heads/e1/e2/r1 &&
213 + git update-ref -d HEAD &&
214 + ! test -e .git/refs/heads/e1/e2 &&
215 + ! test -e .git/logs/refs/heads/e1/e2 &&
216 + test -f .git/refs/heads/e1/r2 &&
217 + test -f .git/logs/refs/heads/e1/r2 &&
218 + test -f .git/logs/HEAD
219 +'
220 +
221 cat >expect <<EOF
222 $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
223 $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch