Skip to content

Commit 2a485da

Browse files
committed
refs: update worktree HEADs when renaming branches
Whenever we rename a branch, we update the repository's symbolic HEAD reference if it currently points to the branch that is to be renamed. But with the introduction of worktrees, we also have to iterate over all HEADs of linked worktrees to adjust them. Do so.
1 parent 38fc5ab commit 2a485da

File tree

3 files changed

+102
-8
lines changed

3 files changed

+102
-8
lines changed

src/refs.c

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -614,20 +614,53 @@ int git_reference_symbolic_set_target(
614614
out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
615615
}
616616

617+
typedef struct {
618+
const char *old_name;
619+
git_refname_t new_name;
620+
} rename_cb_data;
621+
622+
static int update_wt_heads(git_repository *repo, const char *path, void *payload)
623+
{
624+
rename_cb_data *data = (rename_cb_data *) payload;
625+
git_reference *head;
626+
char *gitdir = NULL;
627+
int error = 0;
628+
629+
if (git_reference__read_head(&head, repo, path) < 0 ||
630+
git_reference_type(head) != GIT_REF_SYMBOLIC ||
631+
git__strcmp(head->target.symbolic, data->old_name) != 0 ||
632+
(gitdir = git_path_dirname(path)) == NULL)
633+
goto out;
634+
635+
/* Update HEAD it was pointing to the reference being renamed */
636+
if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) {
637+
giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
638+
goto out;
639+
}
640+
641+
out:
642+
git_reference_free(head);
643+
git__free(gitdir);
644+
645+
return error;
646+
}
647+
617648
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
618649
const git_signature *signature, const char *message)
619650
{
651+
git_repository *repo;
620652
git_refname_t normalized;
621653
bool should_head_be_updated = false;
622654
int error = 0;
623655

624656
assert(ref && new_name && signature);
625657

658+
repo = git_reference_owner(ref);
659+
626660
if ((error = reference_normalize_for_repo(
627-
normalized, git_reference_owner(ref), new_name, true)) < 0)
661+
normalized, repo, new_name, true)) < 0)
628662
return error;
629663

630-
631664
/* Check if we have to update HEAD. */
632665
if ((error = git_branch_is_head(ref)) < 0)
633666
return error;
@@ -637,14 +670,18 @@ static int reference__rename(git_reference **out, git_reference *ref, const char
637670
if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
638671
return error;
639672

640-
/* Update HEAD it was pointing to the reference being renamed */
641-
if (should_head_be_updated &&
642-
(error = git_repository_set_head(ref->db->repo, normalized)) < 0) {
643-
giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
644-
return error;
673+
/* Update HEAD if it was pointing to the reference being renamed */
674+
if (should_head_be_updated) {
675+
error = git_repository_set_head(ref->db->repo, normalized);
676+
} else {
677+
rename_cb_data payload;
678+
payload.old_name = ref->name;
679+
memcpy(&payload.new_name, &normalized, sizeof(normalized));
680+
681+
error = git_repository_foreach_head(repo, update_wt_heads, &payload);
645682
}
646683

647-
return 0;
684+
return error;
648685
}
649686

650687

tests/worktree/refs.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,20 @@ void test_worktree_refs__delete_succeeds_after_pruning_worktree(void)
131131
git_reference_free(branch);
132132
}
133133

134+
void test_worktree_refs__renaming_reference_updates_worktree_heads(void)
135+
{
136+
git_reference *head, *branch, *renamed;
137+
138+
cl_git_pass(git_branch_lookup(&branch, fixture.repo,
139+
"testrepo-worktree", GIT_BRANCH_LOCAL));
140+
cl_git_pass(git_reference_rename(&renamed, branch, "refs/heads/renamed", 0, NULL));
141+
cl_git_pass(git_repository_head(&head, fixture.worktree));
142+
143+
git_reference_free(head);
144+
git_reference_free(branch);
145+
git_reference_free(renamed);
146+
}
147+
134148
void test_worktree_refs__creating_refs_uses_commondir(void)
135149
{
136150
git_reference *head, *branch, *lookup;

tests/worktree/worktree.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,3 +486,46 @@ void test_worktree_worktree__prune_both(void)
486486

487487
git_worktree_free(wt);
488488
}
489+
490+
static int read_head_ref(git_repository *repo, const char *path, void *payload)
491+
{
492+
git_vector *refs = (git_vector *) payload;
493+
git_reference *head;
494+
495+
GIT_UNUSED(repo);
496+
497+
cl_git_pass(git_reference__read_head(&head, repo, path));
498+
499+
git_vector_insert(refs, head);
500+
501+
return 0;
502+
}
503+
504+
void test_worktree_worktree__foreach_head_gives_same_results_in_wt_and_repo(void)
505+
{
506+
git_vector repo_refs = GIT_VECTOR_INIT, worktree_refs = GIT_VECTOR_INIT;
507+
git_reference *heads[2];
508+
size_t i;
509+
510+
cl_git_pass(git_reference_lookup(&heads[0], fixture.repo, GIT_HEAD_FILE));
511+
cl_git_pass(git_reference_lookup(&heads[1], fixture.worktree, GIT_HEAD_FILE));
512+
513+
cl_git_pass(git_repository_foreach_head(fixture.repo, read_head_ref, &repo_refs));
514+
cl_git_pass(git_repository_foreach_head(fixture.worktree, read_head_ref, &worktree_refs));
515+
516+
cl_assert_equal_i(repo_refs.length, ARRAY_SIZE(heads));
517+
cl_assert_equal_i(worktree_refs.length, ARRAY_SIZE(heads));
518+
519+
for (i = 0; i < ARRAY_SIZE(heads); i++) {
520+
cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
521+
cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
522+
cl_assert_equal_s(heads[i]->name, ((git_reference *) worktree_refs.contents[i])->name);
523+
524+
git_reference_free(heads[i]);
525+
git_reference_free(repo_refs.contents[i]);
526+
git_reference_free(worktree_refs.contents[i]);
527+
}
528+
529+
git_vector_free(&repo_refs);
530+
git_vector_free(&worktree_refs);
531+
}

0 commit comments

Comments
 (0)