Commit 99797c96cd57a616ada009a2359390c605d33929

Carlos Martín Nieto 2014-03-19T18:14:35

reflog: handle symref chains Given HEAD -> master -> foo, when updating foo's reflog we should also update HEAD's, as it's considered the current branch.

diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 9051132..25316fe 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -993,23 +993,53 @@ static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref
 {
 	int error;
 	git_oid old_id = {{0}};
-	git_reference *head;
+	git_reference *tmp = NULL, *head = NULL, *peeled = NULL;
+	const char *name;
+
+	if (ref->type == GIT_REF_SYMBOLIC)
+		return 0;
 
 	/* if we can't resolve, we use {0}*40 as old id */
 	git_reference_name_to_id(&old_id, backend->repo, ref->name);
 
-	if ((error = git_reference_lookup(&head, backend->repo, "HEAD")) < 0)
+	if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0)
 		return error;
 
 	if (git_reference_type(head) == GIT_REF_OID)
 		goto cleanup;
 
-	if (strcmp(git_reference_symbolic_target(head), ref->name))
+	if ((error = git_reference_lookup(&tmp, backend->repo, GIT_HEAD_FILE)) < 0)
+		goto cleanup;
+
+	/* Go down the symref chain until we find the branch */
+	while (git_reference_type(tmp) == GIT_REF_SYMBOLIC) {
+		error = git_reference_lookup(&peeled, backend->repo, git_reference_symbolic_target(tmp));
+		if (error < 0)
+			break;
+
+		git_reference_free(tmp);
+		tmp = peeled;
+	}
+
+	if (error == GIT_ENOTFOUND) {
+		error = 0;
+		name = git_reference_symbolic_target(tmp);
+	} else if (error < 0) {
+		goto cleanup;
+	} else {
+		name = git_reference_name(tmp);
+	}
+
+	if (error < 0)
+		goto cleanup;
+
+	if (strcmp(name, ref->name))
 		goto cleanup;
 
 	error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message);
 
 cleanup:
+	git_reference_free(tmp);
 	git_reference_free(head);
 	return error;
 }
diff --git a/tests/repo/head.c b/tests/repo/head.c
index d68a5a6..79892a3 100644
--- a/tests/repo/head.c
+++ b/tests/repo/head.c
@@ -416,3 +416,54 @@ void test_repo_head__branch_birth(void)
 	git_signature_free(sig);
 
 }
+
+static size_t entrycount(git_repository *repo, const char *name)
+{
+	git_reflog *log;
+	size_t ret;
+
+	cl_git_pass(git_reflog_read(&log, repo, name));
+	ret = git_reflog_entrycount(log);
+	git_reflog_free(log);
+
+	return ret;
+}
+
+void test_repo_head__symref_chain(void)
+{
+	git_signature *sig;
+	git_oid id;
+	git_tree *tree;
+	git_reference *ref;
+	const char *msg;
+	size_t nentries, nentries_master;
+
+	nentries = entrycount(repo, GIT_HEAD_FILE);
+
+	cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+	cl_git_pass(git_repository_head(&ref, repo));
+	cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJ_TREE));
+	git_reference_free(ref);
+
+	nentries_master = entrycount(repo, "refs/heads/master");
+
+	msg = "message 1";
+	cl_git_pass(git_reference_symbolic_create(&ref, repo, "refs/heads/master", "refs/heads/foo", 1, sig, msg));
+	git_reference_free(ref);
+
+	cl_assert_equal_i(0, entrycount(repo, "refs/heads/foo"));
+	cl_assert_equal_i(nentries, entrycount(repo, GIT_HEAD_FILE));
+	cl_assert_equal_i(nentries_master, entrycount(repo, "refs/heads/master"));
+
+	msg = "message 2";
+	cl_git_pass(git_commit_create(&id, repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
+	git_tree_free(tree);
+
+	cl_assert_equal_i(1, entrycount(repo, "refs/heads/foo"));
+	cl_assert_equal_i(nentries +1, entrycount(repo, GIT_HEAD_FILE));
+	cl_assert_equal_i(nentries_master, entrycount(repo, "refs/heads/master"));
+
+	git_signature_free(sig);
+
+}