summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2017-08-24 17:41:24 +0700
committerJunio C Hamano <gitster@pobox.com>2017-08-24 14:15:18 -0700
commit31824d180d4f975e6b130cd9a33a9a0baef3d7ca (patch)
tree6cc525856e65e474d9c2487102532114f989f0e1
parentd026a25657cbe15ceb6bcb5d5047a36a0a70b33e (diff)
downloadgit-nd/worktree-kill-parse-ref.tar.gz
branch: fix branch renaming not updating HEADs correctlynd/worktree-kill-parse-ref
There are two bugs that sort of work together and cause problems. Let's start with one in replace_each_worktree_head_symref. Before fa099d2322 (worktree.c: kill parse_ref() in favor of refs_resolve_ref_unsafe() - 2017-04-24), this code looks like this: if (strcmp(oldref, worktrees[i]->head_ref)) continue; set_worktree_head_symref(...); After fa099d2322, it is possible that head_ref can be NULL. However, the updated code takes the wrong exit. In the error case (NULL head_ref), we should "continue;" to the next worktree. The updated code makes us _skip_ "continue;" and update HEAD anyway. The NULL head_ref is triggered by the second bug in add_head_info (in the same commit). With the flag RESOLVE_REF_READING, resolve_ref_unsafe() will abort if it cannot resolve the target ref. For orphan checkouts, HEAD always points to an unborned branch, resolving target ref will always fail. Now we have NULL head_ref. Now we always update HEAD. Correct the logic in replace_ function so that we don't accidentally update HEAD on error. As it turns out, correcting the logic bug above breaks branch renaming completely, thanks to the second bug. "git branch -[Mm]" does two steps (on a normal checkout, no orphan!): - rename the branch on disk (e.g. refs/heads/abc to refs/heads/def) - update HEAD if it points to the branch being renamed. At the second step, since the branch pointed to by HEAD (e.g. "abc") no longer exists on disk, we run into a temporary orphan checkout situation that has been just corrected to _not_ update HEAD. But we need to update HEAD since it's not actually an orphan checkout. We need to update HEAD to move out of that orphan state. Correct add_head_info(), remove RESOLVE_REF_READING flag. With the flag gone, we should always return good "head_ref" in orphan checkouts (either temporary or permanent). With good head_ref, things start to work again. Noticed-by: Nish Aravamudan <nish.aravamudan@canonical.com> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r--branch.c5
-rwxr-xr-xt/t3200-branch.sh13
-rw-r--r--worktree.c2
3 files changed, 17 insertions, 3 deletions
diff --git a/branch.c b/branch.c
index 69d5eea84b..dc5c8aabde 100644
--- a/branch.c
+++ b/branch.c
@@ -357,8 +357,9 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref,
if (worktrees[i]->is_detached)
continue;
- if (worktrees[i]->head_ref &&
- strcmp(oldref, worktrees[i]->head_ref))
+ if (!worktrees[i]->head_ref)
+ continue;
+ if (strcmp(oldref, worktrees[i]->head_ref))
continue;
refs = get_worktree_ref_store(worktrees[i]);
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 9f353c0efc..c5c371888b 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -145,6 +145,19 @@ test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD'
grep "^0\{40\}.*$msg$" .git/logs/HEAD
'
+test_expect_success 'git branch -M should leave orphaned HEAD alone' '
+ git init orphan &&
+ (
+ cd orphan &&
+ test_commit initial &&
+ git checkout --orphan lonely &&
+ grep lonely .git/HEAD &&
+ test_path_is_missing .git/refs/head/lonely &&
+ git branch -M master mistress &&
+ grep lonely .git/HEAD
+ )
+'
+
test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' '
git checkout master &&
git worktree add -b baz bazdir &&
diff --git a/worktree.c b/worktree.c
index c1ec334b06..7bc36f4343 100644
--- a/worktree.c
+++ b/worktree.c
@@ -29,7 +29,7 @@ static void add_head_info(struct worktree *wt)
target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
"HEAD",
- RESOLVE_REF_READING,
+ 0,
wt->head_sha1, &flags);
if (!target)
return;