When multiple worktrees are being used, checkout/switch check that the target branch is not already checked out with code that evolved from 8d9fdd7087 (worktree.c: check whether branch is rebased in another worktree, 2016-04-22), but that logic wasn't valid for -B/-C Avoid reusing the same `branch_info` structure for the checks and assumming that it will be rejected later if is a new branch that already exists as that doesn't apply to -B/-C. Reported-by: Jinwook Jeong <vustthat@xxxxxxxxx> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@xxxxxxxxx> --- builtin/checkout.c | 22 ++++++++++++++++------ t/t2400-worktree-add.sh | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 3fa29a08ee..94dcd617ef 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1474,7 +1474,8 @@ static void die_if_some_operation_in_progress(void) } static int checkout_branch(struct checkout_opts *opts, - struct branch_info *new_branch_info) + struct branch_info *new_branch_info, + struct branch_info *check_branch_info) { if (opts->pathspec.nr) die(_("paths cannot be used with switching branches")); @@ -1533,13 +1534,12 @@ static int checkout_branch(struct checkout_opts *opts, if (!opts->can_switch_when_in_progress) die_if_some_operation_in_progress(); - if (new_branch_info->path && !opts->force_detach && !opts->new_branch && - !opts->ignore_other_worktrees) { + if (check_branch_info->path && !opts->force_detach && !opts->ignore_other_worktrees) { int flag; char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag); if (head_ref && - (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path))) - die_if_checked_out(new_branch_info->path, 1); + (!(flag & REF_ISSYMREF) || strcmp(head_ref, check_branch_info->path))) + die_if_checked_out(check_branch_info->path, 1); free(head_ref); } @@ -1628,6 +1628,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, struct branch_info *new_branch_info) { int parseopt_flags = 0; + struct branch_info check_branch_info = { 0 }; opts->overwrite_ignore = 1; opts->prefix = prefix; @@ -1739,6 +1740,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, !opts->new_branch; int n = parse_branchname_arg(argc, argv, dwim_ok, new_branch_info, opts, &rev); + check_branch_info = *new_branch_info; argv += n; argc -= n; } else if (!opts->accept_ref && opts->from_treeish) { @@ -1751,8 +1753,16 @@ static int checkout_main(int argc, const char **argv, const char *prefix, opts, &rev, opts->from_treeish); + check_branch_info = *new_branch_info; if (!opts->source_tree) die(_("reference is not a tree: %s"), opts->from_treeish); + } else if (opts->new_branch) { + struct object_id rev; + + if (!get_oid_mb(opts->new_branch, &rev)) + setup_new_branch_info_and_source_tree(&check_branch_info, + opts, &rev, + opts->new_branch); } if (argc) { @@ -1819,7 +1829,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix, if (opts->patch_mode || opts->pathspec.nr) return checkout_paths(opts, new_branch_info); else - return checkout_branch(opts, new_branch_info); + return checkout_branch(opts, new_branch_info, &check_branch_info); } int cmd_checkout(int argc, const char **argv, const char *prefix) diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh index d587e0b20d..283ba7607e 100755 --- a/t/t2400-worktree-add.sh +++ b/t/t2400-worktree-add.sh @@ -125,6 +125,13 @@ test_expect_success 'die the same branch is already checked out' ' ) ' +test_expect_success 'die the same branch is already checked out (checkout -B)' ' + ( + cd here && + test_must_fail git checkout -B newmain + ) +' + test_expect_success SYMLINKS 'die the same branch is already checked out (symlink)' ' head=$(git -C there rev-parse --git-path HEAD) && ref=$(git -C there symbolic-ref HEAD) && @@ -147,6 +154,13 @@ test_expect_success 'not die on re-checking out current branch' ' ) ' +test_expect_success 'not die on re-checking out current branch (checkout -B)' ' + ( + cd there && + git checkout -B newmain + ) +' + test_expect_success '"add" from a bare repo' ' ( git clone --bare . bare && -- 2.39.0.1.g119e9c6876.dirty