Skip to content

Commit 6b2ee4a

Browse files
committed
Merge branch 'cb/checkout-same-branch-twice' into seen
"git checkout -B $branch" failed to protect against checking out a branch that is checked out elsewhere, unlike "git branch -f" did. * cb/checkout-same-branch-twice: checkout/switch: disallow checking out same branch in multiple worktrees
2 parents f6bb907 + 78bb251 commit 6b2ee4a

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

builtin/checkout.c

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,7 +1476,8 @@ static void die_if_some_operation_in_progress(void)
14761476
}
14771477

14781478
static int checkout_branch(struct checkout_opts *opts,
1479-
struct branch_info *new_branch_info)
1479+
struct branch_info *new_branch_info,
1480+
char *check_branch_path)
14801481
{
14811482
if (opts->pathspec.nr)
14821483
die(_("paths cannot be used with switching branches"));
@@ -1535,13 +1536,13 @@ static int checkout_branch(struct checkout_opts *opts,
15351536
if (!opts->can_switch_when_in_progress)
15361537
die_if_some_operation_in_progress();
15371538

1538-
if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
1539-
!opts->ignore_other_worktrees) {
1539+
if (!opts->ignore_other_worktrees && !opts->force_detach &&
1540+
check_branch_path && ref_exists(check_branch_path)) {
15401541
int flag;
15411542
char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
1542-
if (head_ref &&
1543-
(!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
1544-
die_if_checked_out(new_branch_info->path, 1);
1543+
if (opts->new_branch_force || (head_ref &&
1544+
(!(flag & REF_ISSYMREF) || strcmp(head_ref, check_branch_path))))
1545+
die_if_checked_out(check_branch_path, 1);
15451546
free(head_ref);
15461547
}
15471548

@@ -1629,7 +1630,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
16291630
const char * const usagestr[],
16301631
struct branch_info *new_branch_info)
16311632
{
1633+
int ret;
16321634
int parseopt_flags = 0;
1635+
char *check_branch_path = NULL;
16331636

16341637
opts->overwrite_ignore = 1;
16351638
opts->prefix = prefix;
@@ -1719,6 +1722,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17191722
opts->new_branch = argv0 + 1;
17201723
}
17211724

1725+
if (opts->new_branch && !opts->ignore_other_worktrees) {
1726+
struct strbuf buf = STRBUF_INIT;
1727+
1728+
strbuf_branchname(&buf, opts->new_branch, INTERPRET_BRANCH_LOCAL);
1729+
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
1730+
check_branch_path = strbuf_detach(&buf, NULL);
1731+
}
17221732
/*
17231733
* Extract branch name from command line arguments, so
17241734
* all that is left is pathspecs.
@@ -1743,6 +1753,9 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
17431753
new_branch_info, opts, &rev);
17441754
argv += n;
17451755
argc -= n;
1756+
1757+
if (!opts->ignore_other_worktrees && !check_branch_path && new_branch_info->path)
1758+
check_branch_path = xstrdup(new_branch_info->path);
17461759
} else if (!opts->accept_ref && opts->from_treeish) {
17471760
struct object_id rev;
17481761

@@ -1819,9 +1832,12 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
18191832
}
18201833

18211834
if (opts->patch_mode || opts->pathspec.nr)
1822-
return checkout_paths(opts, new_branch_info);
1835+
ret = checkout_paths(opts, new_branch_info);
18231836
else
1824-
return checkout_branch(opts, new_branch_info);
1837+
ret = checkout_branch(opts, new_branch_info, check_branch_path);
1838+
1839+
free(check_branch_path);
1840+
return ret;
18251841
}
18261842

18271843
int cmd_checkout(int argc, const char **argv, const char *prefix)

t/t2400-worktree-add.sh

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,32 +118,52 @@ test_expect_success '"add" worktree creating new branch' '
118118
)
119119
'
120120

121-
test_expect_success 'die the same branch is already checked out' '
121+
test_expect_success 'die if the same branch is already checked out' '
122122
(
123123
cd here &&
124-
test_must_fail git checkout newmain
124+
test_must_fail git checkout newmain &&
125+
test_must_fail git checkout -B newmain &&
126+
test_must_fail git switch newmain &&
127+
test_must_fail git switch -C newmain
125128
)
126129
'
127130

128-
test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' '
131+
test_expect_success SYMLINKS 'die if the same branch is already checked out (symlink)' '
129132
head=$(git -C there rev-parse --git-path HEAD) &&
130133
ref=$(git -C there symbolic-ref HEAD) &&
131134
rm "$head" &&
132135
ln -s "$ref" "$head" &&
133136
test_must_fail git -C here checkout newmain
134137
'
135138

136-
test_expect_success 'not die the same branch is already checked out' '
139+
test_expect_success 'allow creating multiple worktrees for same branch with force' '
140+
git worktree add --force anothernewmain newmain
141+
'
142+
143+
test_expect_success 'allow checkout/reset from the conflicted branch' '
137144
(
138145
cd here &&
139-
git worktree add --force anothernewmain newmain
146+
git checkout -b conflictedmain newmain &&
147+
git checkout -B conflictedmain newmain &&
148+
git switch -C conflictedmain newmain
149+
)
150+
'
151+
152+
test_expect_success 'and not die on re-checking out current branch even if conflicted' '
153+
(
154+
cd there &&
155+
git checkout newmain &&
156+
git switch newmain
140157
)
141158
'
142159

143-
test_expect_success 'not die on re-checking out current branch' '
160+
test_expect_failure 'unless using force without --ignore-other-worktrees' '
144161
(
145162
cd there &&
146-
git checkout newmain
163+
test_must_fail git checkout -B newmain &&
164+
test_must_fail git switch -C newmain &&
165+
git checkout --ignore-other-worktrees -B newmain &&
166+
git switch --ignore-other-worktrees -C newmain
147167
)
148168
'
149169

0 commit comments

Comments
 (0)