Commit 6f13a30565fc3891c0df161945097b316945fca7

Carlos Martín Nieto 2013-11-17T23:26:49

reflog: write to the reflog following git's rules git-core only writes to the reflogs of HEAD, refs/heads/ and, refs/notes/ or if there is already a reflog in place. Adjust our code to follow these semantics.

diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 2f7b434..9f23de2 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -911,6 +911,22 @@ fail:
 }
 
 static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_signature *author, const char *message);
+static int has_reflog(git_repository *repo, const char *name);
+
+/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */
+static bool should_write_reflog(git_repository *repo, const char *name)
+{
+	if (has_reflog(repo, name))
+		return 1;
+
+	if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR) ||
+	    !git__strcmp(name, GIT_HEAD_FILE) ||
+	    !git__prefixcmp(name, GIT_REFS_REMOTES_DIR) ||
+	    !git__prefixcmp(name, GIT_REFS_NOTES_DIR))
+		return 1;
+
+	return 0;
+}
 
 static int refdb_fs_backend__write(
 	git_refdb_backend *_backend,
@@ -933,7 +949,8 @@ static int refdb_fs_backend__write(
 	if ((error = loose_lock(&file, backend, ref)) < 0)
 		return error;
 
-	if ((error = reflog_append(backend, ref, who, message)) < 0) {
+	if (should_write_reflog(backend->repo, ref->name) &&
+	    (error = reflog_append(backend, ref, who, message)) < 0) {
 		git_filebuf_cleanup(&file);
 		return error;
 	}
@@ -1228,6 +1245,21 @@ GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const 
 	return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name);
 }
 
+static int has_reflog(git_repository *repo, const char *name)
+{
+	int ret = 0;
+	git_buf path = GIT_BUF_INIT;
+
+	if (retrieve_reflog_path(&path, repo, name) < 0)
+		goto cleanup;
+
+	ret = git_path_isfile(git_buf_cstr(&path));
+
+cleanup:
+	git_buf_free(&path);
+	return ret;
+}
+
 static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
 {
 	int error = -1;
@@ -1338,7 +1370,6 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo
 	int error = -1;
 	unsigned int i;
 	git_reflog_entry *entry;
-	git_repository *repo;
 	refdb_fs_backend *backend;
 	git_buf log = GIT_BUF_INIT;
 	git_filebuf fbuf = GIT_FILEBUF_INIT;
@@ -1346,7 +1377,6 @@ static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflo
 	assert(_backend && reflog);
 
 	backend = (refdb_fs_backend *) _backend;
-	repo = backend->repo;
 
 	if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
 		return -1;
diff --git a/src/refs.h b/src/refs.h
index 80c7703..4d5b6da 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -19,6 +19,7 @@
 #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
 #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
 #define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/"
+#define GIT_REFS_NOTES_DIR GIT_REFS_DIR "notes/"
 #define GIT_REFS_DIR_MODE 0777
 #define GIT_REFS_FILE_MODE 0666
 
diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
index 9ac15d5..6c9375d 100644
--- a/tests/refs/reflog/reflog.c
+++ b/tests/refs/reflog/reflog.c
@@ -187,3 +187,39 @@ void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC
 	cl_assert_equal_i(GIT_EINVALIDSPEC,
 			  git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id"));
 }
+
+void test_refs_reflog_reflog__write_only_std_locations(void)
+{
+	git_reference *ref;
+	git_oid id;
+
+	git_oid_fromstr(&id, current_master_tip);
+
+	cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/foo", &id, 1));
+	git_reference_free(ref);
+	cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1));
+	git_reference_free(ref);
+	cl_git_pass(git_reference_create(&ref, g_repo, "refs/notes/foo", &id, 1));
+	git_reference_free(ref);
+
+	assert_has_reflog(true, "refs/heads/foo");
+	assert_has_reflog(false, "refs/tags/foo");
+	assert_has_reflog(true, "refs/notes/foo");
+
+}
+
+void test_refs_reflog_reflog__write_when_explicitly_active(void)
+{
+	git_reference *ref;
+	git_oid id;
+	git_buf path = GIT_BUF_INIT;
+
+	git_oid_fromstr(&id, current_master_tip);
+	cl_git_pass(git_buf_join(&path, '/', git_repository_path(g_repo), "logs/refs/tags/foo"));
+	cl_git_pass(git_futils_mkpath2file(git_buf_cstr(&path), 0777));
+	cl_git_mkfile(git_buf_cstr(&path), "");
+
+	cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1));
+	git_reference_free(ref);
+	assert_has_reflog(true, "refs/tags/foo");
+}