summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/refs.c53
-rw-r--r--tests/worktree/refs.c14
-rw-r--r--tests/worktree/worktree.c43
3 files changed, 102 insertions, 8 deletions
diff --git a/src/refs.c b/src/refs.c
index a53d094fc..31410b75f 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -614,20 +614,53 @@ int git_reference_symbolic_set_target(
out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
}
+typedef struct {
+ const char *old_name;
+ git_refname_t new_name;
+} rename_cb_data;
+
+static int update_wt_heads(git_repository *repo, const char *path, void *payload)
+{
+ rename_cb_data *data = (rename_cb_data *) payload;
+ git_reference *head;
+ char *gitdir = NULL;
+ int error = 0;
+
+ if (git_reference__read_head(&head, repo, path) < 0 ||
+ git_reference_type(head) != GIT_REF_SYMBOLIC ||
+ git__strcmp(head->target.symbolic, data->old_name) != 0 ||
+ (gitdir = git_path_dirname(path)) == NULL)
+ goto out;
+
+ /* Update HEAD it was pointing to the reference being renamed */
+ if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) {
+ giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
+ goto out;
+ }
+
+out:
+ git_reference_free(head);
+ git__free(gitdir);
+
+ return error;
+}
+
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
const git_signature *signature, const char *message)
{
+ git_repository *repo;
git_refname_t normalized;
bool should_head_be_updated = false;
int error = 0;
assert(ref && new_name && signature);
+ repo = git_reference_owner(ref);
+
if ((error = reference_normalize_for_repo(
- normalized, git_reference_owner(ref), new_name, true)) < 0)
+ normalized, repo, new_name, true)) < 0)
return error;
-
/* Check if we have to update HEAD. */
if ((error = git_branch_is_head(ref)) < 0)
return error;
@@ -637,14 +670,18 @@ static int reference__rename(git_reference **out, git_reference *ref, const char
if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
return error;
- /* Update HEAD it was pointing to the reference being renamed */
- if (should_head_be_updated &&
- (error = git_repository_set_head(ref->db->repo, normalized)) < 0) {
- giterr_set(GITERR_REFERENCE, "failed to update HEAD after renaming reference");
- return error;
+ /* Update HEAD if it was pointing to the reference being renamed */
+ if (should_head_be_updated) {
+ error = git_repository_set_head(ref->db->repo, normalized);
+ } else {
+ rename_cb_data payload;
+ payload.old_name = ref->name;
+ memcpy(&payload.new_name, &normalized, sizeof(normalized));
+
+ error = git_repository_foreach_head(repo, update_wt_heads, &payload);
}
- return 0;
+ return error;
}
diff --git a/tests/worktree/refs.c b/tests/worktree/refs.c
index cb6bfb559..95b173ef5 100644
--- a/tests/worktree/refs.c
+++ b/tests/worktree/refs.c
@@ -131,6 +131,20 @@ void test_worktree_refs__delete_succeeds_after_pruning_worktree(void)
git_reference_free(branch);
}
+void test_worktree_refs__renaming_reference_updates_worktree_heads(void)
+{
+ git_reference *head, *branch, *renamed;
+
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "testrepo-worktree", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_reference_rename(&renamed, branch, "refs/heads/renamed", 0, NULL));
+ cl_git_pass(git_repository_head(&head, fixture.worktree));
+
+ git_reference_free(head);
+ git_reference_free(branch);
+ git_reference_free(renamed);
+}
+
void test_worktree_refs__creating_refs_uses_commondir(void)
{
git_reference *head, *branch, *lookup;
diff --git a/tests/worktree/worktree.c b/tests/worktree/worktree.c
index 6e90e6ac0..73991bff7 100644
--- a/tests/worktree/worktree.c
+++ b/tests/worktree/worktree.c
@@ -486,3 +486,46 @@ void test_worktree_worktree__prune_both(void)
git_worktree_free(wt);
}
+
+static int read_head_ref(git_repository *repo, const char *path, void *payload)
+{
+ git_vector *refs = (git_vector *) payload;
+ git_reference *head;
+
+ GIT_UNUSED(repo);
+
+ cl_git_pass(git_reference__read_head(&head, repo, path));
+
+ git_vector_insert(refs, head);
+
+ return 0;
+}
+
+void test_worktree_worktree__foreach_head_gives_same_results_in_wt_and_repo(void)
+{
+ git_vector repo_refs = GIT_VECTOR_INIT, worktree_refs = GIT_VECTOR_INIT;
+ git_reference *heads[2];
+ size_t i;
+
+ cl_git_pass(git_reference_lookup(&heads[0], fixture.repo, GIT_HEAD_FILE));
+ cl_git_pass(git_reference_lookup(&heads[1], fixture.worktree, GIT_HEAD_FILE));
+
+ cl_git_pass(git_repository_foreach_head(fixture.repo, read_head_ref, &repo_refs));
+ cl_git_pass(git_repository_foreach_head(fixture.worktree, read_head_ref, &worktree_refs));
+
+ cl_assert_equal_i(repo_refs.length, ARRAY_SIZE(heads));
+ cl_assert_equal_i(worktree_refs.length, ARRAY_SIZE(heads));
+
+ for (i = 0; i < ARRAY_SIZE(heads); i++) {
+ cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
+ cl_assert_equal_s(heads[i]->name, ((git_reference *) repo_refs.contents[i])->name);
+ cl_assert_equal_s(heads[i]->name, ((git_reference *) worktree_refs.contents[i])->name);
+
+ git_reference_free(heads[i]);
+ git_reference_free(repo_refs.contents[i]);
+ git_reference_free(worktree_refs.contents[i]);
+ }
+
+ git_vector_free(&repo_refs);
+ git_vector_free(&worktree_refs);
+}