Merge pull request #4833 from csware/drop-empty-dirs Remove empty (sub-)directories when deleting refs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 958d508..46f552e 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1313,6 +1313,43 @@ on_error:
return error;
}
+static void refdb_fs_backend__try_delete_empty_ref_hierarchie(
+ refdb_fs_backend *backend,
+ const char *ref_name,
+ bool reflog)
+{
+ git_buf relative_path = GIT_BUF_INIT;
+ git_buf base_path = GIT_BUF_INIT;
+ size_t commonlen;
+
+ assert(backend && ref_name);
+
+ if (git_buf_sets(&relative_path, ref_name) < 0)
+ goto cleanup;
+
+ git_path_squash_slashes(&relative_path);
+ if ((commonlen = git_path_common_dirlen("refs/heads/", git_buf_cstr(&relative_path))) == strlen("refs/heads/") ||
+ (commonlen = git_path_common_dirlen("refs/tags/", git_buf_cstr(&relative_path))) == strlen("refs/tags/") ||
+ (commonlen = git_path_common_dirlen("refs/remotes/", git_buf_cstr(&relative_path))) == strlen("refs/remotes/")) {
+
+ git_buf_truncate(&relative_path, commonlen);
+
+ if (reflog) {
+ if (git_buf_join3(&base_path, '/', backend->commonpath, GIT_REFLOG_DIR, git_buf_cstr(&relative_path)) < 0)
+ goto cleanup;
+ } else {
+ if (git_buf_joinpath(&base_path, backend->commonpath, git_buf_cstr(&relative_path)) < 0)
+ goto cleanup;
+ }
+
+ git_futils_rmdir_r(ref_name + commonlen, git_buf_cstr(&base_path), GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
+ }
+
+cleanup:
+ git_buf_dispose(&relative_path);
+ git_buf_dispose(&base_path);
+}
+
static int refdb_fs_backend__delete(
git_refdb_backend *_backend,
const char *ref_name,
@@ -1393,7 +1430,8 @@ static int refdb_fs_backend__delete_tail(
cleanup:
git_buf_dispose(&loose_path);
git_filebuf_cleanup(file);
-
+ if (loose_deleted)
+ refdb_fs_backend__try_delete_empty_ref_hierarchie(backend, ref_name, false);
return error;
}
@@ -2021,8 +2059,10 @@ static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name
error = retrieve_reflog_path(&path, repo, name);
- if (!error && git_path_exists(path.ptr))
+ if (!error && git_path_exists(path.ptr)) {
error = p_unlink(path.ptr);
+ refdb_fs_backend__try_delete_empty_ref_hierarchie(backend, name, true);
+ }
git_buf_dispose(&path);
diff --git a/tests/refs/branches/delete.c b/tests/refs/branches/delete.c
index 8807db2..928adf7 100644
--- a/tests/refs/branches/delete.c
+++ b/tests/refs/branches/delete.c
@@ -2,6 +2,8 @@
#include "refs.h"
#include "repo/repo_helpers.h"
#include "config/config_helpers.h"
+#include "fileops.h"
+#include "reflog.h"
static git_repository *repo;
static git_reference *fake_remote;
@@ -140,3 +142,47 @@ void test_refs_branches_delete__removes_reflog(void)
git_reflog_free(log);
}
+void test_refs_branches_delete__removes_empty_folders(void)
+{
+ const char *commondir = git_repository_commondir(repo);
+ git_oid commit_id;
+ git_commit *commit;
+ git_reference *branch;
+
+ git_reflog *log;
+ git_oid oidzero = {{0}};
+ git_signature *sig;
+
+ git_buf ref_folder = GIT_BUF_INIT;
+ git_buf reflog_folder = GIT_BUF_INIT;
+
+ /* Create a new branch with a nested name */
+ cl_git_pass(git_oid_fromstr(&commit_id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_git_pass(git_branch_create(&branch, repo, "some/deep/ref", commit, 0));
+ git_commit_free(commit);
+
+ /* Ensure the reflog has at least one entry */
+ cl_git_pass(git_signature_now(&sig, "Me", "user@example.com"));
+ cl_git_pass(git_reflog_read(&log, repo, "refs/heads/some/deep/ref"));
+ cl_git_pass(git_reflog_append(log, &oidzero, sig, "message"));
+ cl_assert(git_reflog_entrycount(log) > 0);
+ git_signature_free(sig);
+ git_reflog_free(log);
+
+ cl_git_pass(git_buf_joinpath(&ref_folder, commondir, "refs/heads/some/deep"));
+ cl_git_pass(git_buf_join3(&reflog_folder, '/', commondir, GIT_REFLOG_DIR, "refs/heads/some/deep"));
+
+ cl_assert(git_path_exists(git_buf_cstr(&ref_folder)) == true);
+ cl_assert(git_path_exists(git_buf_cstr(&reflog_folder)) == true);
+
+ cl_git_pass(git_branch_delete(branch));
+
+ cl_assert(git_path_exists(git_buf_cstr(&ref_folder)) == false);
+ cl_assert(git_path_exists(git_buf_cstr(&reflog_folder)) == false);
+
+ git_reference_free(branch);
+ git_buf_dispose(&ref_folder);
+ git_buf_dispose(&reflog_folder);
+}
+