reflog: error when a directory is at reflog path When a non-empty directory exists and prevents the creation of a reflog, provide a more informative error message.
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
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index c550ee9..85b5034 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1788,10 +1788,17 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co
/* If the new branch matches part of the namespace of a previously deleted branch,
* there maybe an obsolete/unused directory (or directory hierarchy) in the way.
*/
- if (git_path_isdir(git_buf_cstr(&path)) &&
- (git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
- error = -1;
- goto cleanup;
+ if (git_path_isdir(git_buf_cstr(&path))) {
+ if ((git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0))
+ error = -1;
+ else if (git_path_isdir(git_buf_cstr(&path))) {
+ giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
+ ref->name);
+ error = GIT_EDIRECTORY;
+ }
+
+ if (error != 0)
+ goto cleanup;
}
error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE);
diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
index ba40c76..fdb1550 100644
--- a/tests/refs/reflog/reflog.c
+++ b/tests/refs/reflog/reflog.c
@@ -143,6 +143,59 @@ void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void)
git_buf_free(&master_log_path);
}
+void test_refs_reflog_reflog__removes_empty_reflog_dir(void)
+{
+ git_reference *ref;
+ git_buf log_path = GIT_BUF_INIT;
+ git_oid id;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL));
+
+ git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head");
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path)));
+
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ /* new ref creation should succeed since new-dir is empty */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_buf_free(&log_path);
+}
+
+void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void)
+{
+ git_reference *ref;
+ git_buf log_path = GIT_BUF_INIT;
+ git_oid id;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head");
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path)));
+
+ /* delete the ref manually, leave the reflog */
+ cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head"));
+
+ /* new ref creation should fail since new-dir contains reflogs still */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_fail_with(GIT_EDIRECTORY, git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_buf_free(&log_path);
+}
+
static void assert_has_reflog(bool expected_result, const char *name)
{
cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name));