Commit 589443883bfb450fb2d04f50df8854d18bf9d176

Edward Thomson 2021-05-16T11:11:56

Merge branch 'zero_oid_in_old' Manually merging #5842

diff --git a/include/git2/refs.h b/include/git2/refs.h
index 8bc99e1..7ebb209 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -97,6 +97,9 @@ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, co
  * of updating does not match the one passed through `current_value`
  * (i.e. if the ref has changed since the user read it).
  *
+ * If `current_value` is all zeros, this function will return GIT_EMODIFIED
+ * if the ref already exists.
+ *
  * @param out Pointer to the newly created reference
  * @param repo Repository where that reference will live
  * @param name The name of the reference
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index ad8068e..0b8e103 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1173,8 +1173,11 @@ static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
 	if (!old_id && !old_target)
 		return 0;
 
-	if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0)
+	if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0) {
+		if (error == GIT_ENOTFOUND && old_id && git_oid_is_zero(old_id))
+			return 0;
 		goto out;
+	}
 
 	/* If the types don't match, there's no way the values do */
 	if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) {
diff --git a/tests/refs/races.c b/tests/refs/races.c
index 04a1bc1..9134bf9 100644
--- a/tests/refs/races.c
+++ b/tests/refs/races.c
@@ -22,6 +22,22 @@ void test_refs_races__cleanup(void)
    cl_git_sandbox_cleanup();
 }
 
+void test_refs_races__create_matching_zero_old(void)
+{
+	git_reference *ref;
+	git_oid id, zero_id;
+
+	git_oid_fromstr(&id, commit_id);
+	git_oid_fromstr(&zero_id, "0000000000000000000000000000000000000000");
+
+	cl_git_fail(git_reference_create_matching(&ref, g_repo, refname, &id, 1, &zero_id, NULL));
+
+	cl_git_pass(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL));
+	cl_git_fail(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL));
+
+	git_reference_free(ref);
+}
+
 void test_refs_races__create_matching(void)
 {
 	git_reference *ref, *ref2, *ref3;