diff options
| -rw-r--r-- | src/refs.c | 53 | ||||
| -rw-r--r-- | tests/worktree/refs.c | 14 | ||||
| -rw-r--r-- | tests/worktree/worktree.c | 43 |
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); +} |
