refs: cleanup directories when deleting packed ref

When deleting a packed ref via 'update-ref -d', a lockfile is made in the directory that would contain the loose copy of that ref, creating any directories in the ref's path that do not exist. When the transaction completes, the lockfile is deleted, but any empty parent directories made when creating the lockfile are left in place. These empty directories are not removed by 'pack-refs' or other housekeeping tasks and will accumulate over time. When deleting a loose ref, we remove all empty parent directories at the end of the transaction. This commit applies the parent directory cleanup logic used when deleting loose refs to packed refs as well. Signed-off-by: Will Chandler <wfc@wfchandler.org> Reviewed-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Will Chandler committed May 8, 2021 at 01:00 UTC 5f03e5126d895283e6d42093dd4f15cbc30011dd
2 files changed +15 -6
refs/files-backend.c
+6 -6
@@ -45,10 +45,10 @@
45 #define REF_UPDATE_VIA_HEAD (1 << 8)
46
47 /*
48 - * Used as a flag in ref_update::flags when the loose reference has
49 - * been deleted.
48 + * Used as a flag in ref_update::flags when a reference has been
49 + * deleted and the ref's parent directories may need cleanup.
50 */
51 -#define REF_DELETED_LOOSE (1 << 9)
51 +#define REF_DELETED_RMDIR (1 << 9)
52
53 struct ref_lock {
54 char *ref_name;
@@ -2852,6 +2852,7 @@ static int files_transaction_finish(struct ref_store *ref_store,
2852
2853 if (update->flags & REF_DELETING &&
2854 !(update->flags & REF_LOG_ONLY)) {
2855 + update->flags |= REF_DELETED_RMDIR;
2856 if (!(update->type & REF_ISPACKED) ||
2857 update->type & REF_ISSYMREF) {
2858 /* It is a loose reference. */
@@ -2861,7 +2862,6 @@ static int files_transaction_finish(struct ref_store *ref_store,
2862 ret = TRANSACTION_GENERIC_ERROR;
2863 goto cleanup;
2864 }
2864 - update->flags |= REF_DELETED_LOOSE;
2865 }
2866 }
2867 }
@@ -2874,9 +2874,9 @@ cleanup:
2874 for (i = 0; i < transaction->nr; i++) {
2875 struct ref_update *update = transaction->updates[i];
2876
2877 - if (update->flags & REF_DELETED_LOOSE) {
2877 + if (update->flags & REF_DELETED_RMDIR) {
2878 /*
2879 - * The loose reference was deleted. Delete any
2879 + * The reference was deleted. Delete any
2880 * empty parent directories. (Note that this
2881 * can only work because we have already
2882 * removed the lockfile.)
t/t1400-update-ref.sh
+9
@@ -1598,4 +1598,13 @@ test_expect_success 'transaction cannot restart ongoing transaction' '
1598 test_must_fail git show-ref --verify refs/heads/restart
1599 '
1600
1601 +test_expect_success 'directory not created deleting packed ref' '
1602 + git branch d1/d2/r1 HEAD &&
1603 + git pack-refs --all &&
1604 + test_path_is_missing .git/refs/heads/d1/d2 &&
1605 + git update-ref -d refs/heads/d1/d2/r1 &&
1606 + test_path_is_missing .git/refs/heads/d1/d2 &&
1607 + test_path_is_missing .git/refs/heads/d1
1608 +'
1609 +
1610 test_done