Skip to content

Commit 4463977

Browse files
mhaggergitster
authored andcommitted
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 <[email protected]> Reviewed-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent a8f0db2 commit 4463977

File tree

3 files changed

+64
-8
lines changed

3 files changed

+64
-8
lines changed

refs/files-backend.c

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2346,7 +2346,6 @@ static void prune_ref(struct ref_to_prune *r)
23462346
}
23472347
ref_transaction_free(transaction);
23482348
strbuf_release(&err);
2349-
try_remove_empty_parents(r->name, REMOVE_EMPTY_PARENTS_REF);
23502349
}
23512350

23522351
static void prune_refs(struct ref_to_prune *r)
@@ -3794,6 +3793,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
37943793
ret = TRANSACTION_GENERIC_ERROR;
37953794
goto cleanup;
37963795
}
3796+
update->flags |= REF_DELETED_LOOSE;
37973797
}
37983798

37993799
if (!(update->flags & REF_ISPRUNING))
@@ -3806,16 +3806,38 @@ static int files_transaction_commit(struct ref_store *ref_store,
38063806
ret = TRANSACTION_GENERIC_ERROR;
38073807
goto cleanup;
38083808
}
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+
38113817
clear_loose_ref_cache(refs);
38123818

38133819
cleanup:
38143820
transaction->state = REF_TRANSACTION_CLOSED;
38153821

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+
38193841
string_list_clear(&refs_to_delete, 0);
38203842
free(head_ref);
38213843
string_list_clear(&affected_refnames, 0);

refs/refs-internal.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@
5555
*/
5656
#define REF_UPDATE_VIA_HEAD 0x100
5757

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+
5864
/*
5965
* Return true iff refname is minimally safe. "Safe" here means that
6066
* deleting a loose reference by this name will not do any damage, for
@@ -158,8 +164,9 @@ struct ref_update {
158164

159165
/*
160166
* 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:
163170
*/
164171
unsigned int flags;
165172

t/t1400-update-ref.sh

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,33 @@ test_expect_success \
191191
git update-ref HEAD'" $A &&
192192
test $A"' = $(cat .git/'"$m"')'
193193

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+
194221
cat >expect <<EOF
195222
$Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creation
196223
$A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000 Switch

0 commit comments

Comments
 (0)