Commit 47ac1187aaa53050c46056a4daf9a01916263da4

Patrick Steinhardt 2020-01-17T08:32:37

Merge pull request #5360 from josharian/fix-5357 refs: refuse to delete HEAD

diff --git a/src/refs.c b/src/refs.c
index 29dd1bd..633d83a 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -145,6 +145,11 @@ int git_reference_delete(git_reference *ref)
 	const git_oid *old_id = NULL;
 	const char *old_target = NULL;
 
+	if (!strcmp(ref->name, "HEAD")) {
+		git_error_set(GIT_ERROR_REFERENCE, "cannot delete HEAD");
+		return GIT_ERROR;
+	}
+
 	if (ref->type == GIT_REFERENCE_DIRECT)
 		old_id = &ref->target.oid;
 	else
diff --git a/tests/iterator/workdir.c b/tests/iterator/workdir.c
index 926cc6a..72f8bf4 100644
--- a/tests/iterator/workdir.c
+++ b/tests/iterator/workdir.c
@@ -620,6 +620,7 @@ void test_iterator_workdir__filesystem2(void)
 		"heads/subtrees",
 		"heads/test",
 		"heads/testrepo-worktree",
+		"symref",
 		"tags/e90810b",
 		"tags/foo/bar",
 		"tags/foo/foo/bar",
@@ -632,7 +633,7 @@ void test_iterator_workdir__filesystem2(void)
 
 	cl_git_pass(git_iterator_for_filesystem(
 		&i, "testrepo/.git/refs", NULL));
-	expect_iterator_items(i, 16, expect_base, 16, expect_base);
+	expect_iterator_items(i, 17, expect_base, 17, expect_base);
 	git_iterator_free(i);
 }
 
diff --git a/tests/refs/delete.c b/tests/refs/delete.c
index a334496..3e99a79 100644
--- a/tests/refs/delete.c
+++ b/tests/refs/delete.c
@@ -105,3 +105,14 @@ void test_refs_delete__remove(void)
 
 	cl_git_fail(git_reference_lookup(&ref, g_repo, packed_test_head_name));
 }
+
+void test_refs_delete__head(void)
+{
+	git_reference *ref;
+
+	/* Check that it is not possible to delete HEAD */
+
+	cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+	cl_git_fail(git_reference_delete(ref));
+	git_reference_free(ref);
+}
diff --git a/tests/refs/list.c b/tests/refs/list.c
index 3e8c82c..725d381 100644
--- a/tests/refs/list.c
+++ b/tests/refs/list.c
@@ -36,7 +36,7 @@ void test_refs_list__all(void)
 	/* We have exactly 12 refs in total if we include the packed ones:
 	 * there is a reference that exists both in the packfile and as
 	 * loose, but we only list it once */
-	cl_assert_equal_i((int)ref_list.count, 18);
+	cl_assert_equal_i((int)ref_list.count, 19);
 
 	git_strarray_free(&ref_list);
 }
@@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten
 		"144344043ba4d4a405da03de3844aa829ae8be0e\n");
 
 	cl_git_pass(git_reference_list(&ref_list, g_repo));
-	cl_assert_equal_i((int)ref_list.count, 18);
+	cl_assert_equal_i((int)ref_list.count, 19);
 
 	git_strarray_free(&ref_list);
 }
diff --git a/tests/refs/races.c b/tests/refs/races.c
index fbecf4a..04a1bc1 100644
--- a/tests/refs/races.c
+++ b/tests/refs/races.c
@@ -74,8 +74,8 @@ void test_refs_races__delete(void)
 	git_reference_free(ref);
 
 	/* We cannot delete a symbolic value that doesn't match */
-	cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
-	cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "HEAD", other_refname, 1, NULL, refname));
+	cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref"));
+	cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "refs/symref", other_refname, 1, NULL, refname));
 	cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
 
 	git_reference_free(ref);
@@ -131,19 +131,19 @@ void test_refs_races__switch_symbolic_to_oid(void)
 	git_oid_fromstr(&other_id, other_commit_id);
 
 	/* Removing a symbolic ref when it's currently direct should fail */
-	cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
-	cl_git_pass(git_reference_create(&ref2, g_repo, "HEAD", &id, 1, NULL));
+	cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref"));
+	cl_git_pass(git_reference_create(&ref2, g_repo, "refs/symref", &id, 1, NULL));
 	cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
 
 	git_reference_free(ref);
 	git_reference_free(ref2);
 
-	cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "HEAD", refname, 1, NULL));
+	cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "refs/symref", refname, 1, NULL));
 	git_reference_free(ref);
 
 	/* Updating a symbolic ref when it's currently direct should fail */
-	cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
-	cl_git_pass(git_reference_create(&ref2, g_repo, "HEAD", &id, 1, NULL));
+	cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref"));
+	cl_git_pass(git_reference_create(&ref2, g_repo, "refs/symref", &id, 1, NULL));
 	cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_set_target(&ref3, ref, other_refname, NULL));
 
 	git_reference_free(ref);
diff --git a/tests/resources/testrepo/.gitted/refs/symref b/tests/resources/testrepo/.gitted/refs/symref
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/symref
@@ -0,0 +1 @@
+ref: refs/heads/master