Skip to content

Commit f979388

Browse files
jfultzpks-t
authored andcommitted
branch: fix forced branch creation on HEAD of a bare repo
The code correctly detects that forced creation of a branch on a nonbare repo should not be able to overwrite a branch which is the HEAD reference. But there's no reason to prevent this on a bare repo, and in fact, git allows this. I.e., git branch -f master new_sha works on a bare repo with HEAD set to master. This change fixes that problem, and updates tests so that, for this case, both the bare and nonbare cases are checked for correct behavior.
1 parent e3298a3 commit f979388

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

src/branch.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,27 +58,28 @@ static int create_branch(
5858
const char *from,
5959
int force)
6060
{
61-
int is_head = 0;
61+
int is_unmovable_head = 0;
6262
git_reference *branch = NULL;
6363
git_buf canonical_branch_name = GIT_BUF_INIT,
6464
log_message = GIT_BUF_INIT;
6565
int error = -1;
66+
int bare = git_repository_is_bare(repository);
6667

6768
assert(branch_name && commit && ref_out);
6869
assert(git_object_owner((const git_object *)commit) == repository);
6970

70-
if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
71+
if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
7172
error = git_branch_is_head(branch);
7273
git_reference_free(branch);
7374
branch = NULL;
7475

7576
if (error < 0)
7677
goto cleanup;
7778

78-
is_head = error;
79+
is_unmovable_head = error;
7980
}
8081

81-
if (is_head && force) {
82+
if (is_unmovable_head && force) {
8283
giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
8384
"the current HEAD of the repository.", branch_name);
8485
error = -1;

tests/refs/branches/create.c

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,14 @@ void test_refs_branches_create__can_force_create_over_an_existing_branch(void)
6565
cl_assert_equal_s("refs/heads/br2", git_reference_name(branch));
6666
}
6767

68-
void test_refs_branches_create__cannot_force_create_over_current_branch(void)
68+
void test_refs_branches_create__cannot_force_create_over_current_branch_in_nonbare_repo(void)
6969
{
7070
const git_oid *oid;
7171
git_reference *branch2;
72+
73+
/* Default repo for these tests is a bare repo, but this test requires a non-bare one */
74+
cl_git_sandbox_cleanup();
75+
repo = cl_git_sandbox_init("testrepo");
7276
retrieve_known_commit(&target, repo);
7377

7478
cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL));
@@ -84,6 +88,26 @@ void test_refs_branches_create__cannot_force_create_over_current_branch(void)
8488
git_reference_free(branch2);
8589
}
8690

91+
void test_refs_branches_create__can_force_create_over_current_branch_in_bare_repo(void)
92+
{
93+
const git_oid *oid;
94+
git_reference *branch2;
95+
retrieve_known_commit(&target, repo);
96+
97+
cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL));
98+
cl_assert_equal_s("refs/heads/master", git_reference_name(branch2));
99+
cl_assert_equal_i(true, git_branch_is_head(branch2));
100+
oid = git_commit_id(target);
101+
102+
cl_git_pass(git_branch_create(&branch, repo, "master", target, 1));
103+
git_reference_free(branch);
104+
branch = NULL;
105+
cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
106+
cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
107+
cl_git_pass(git_oid_cmp(git_reference_target(branch), oid));
108+
git_reference_free(branch2);
109+
}
110+
87111
void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
88112
{
89113
retrieve_known_commit(&target, repo);

0 commit comments

Comments
 (0)