Skip to content

Commit

Permalink
Merge branch 'nd/checkout-dwim-fix'
Browse files Browse the repository at this point in the history
"git checkout frotz" (without any double-dash) avoids ambiguity by
making sure 'frotz' cannot be interpreted as a revision and as a
path at the same time.  This safety has been updated to check also
a unique remote-tracking branch 'frotz' in a remote, when dwimming
to create a local branch 'frotz' out of a remote-tracking branch
'frotz' from a remote.

* nd/checkout-dwim-fix:
  checkout: disambiguate dwim tracking branches and local files
  • Loading branch information
gitster committed Jan 4, 2019
2 parents 0a84724 + be4908f commit 8d7f9db
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 6 deletions.
4 changes: 4 additions & 0 deletions Documentation/git-checkout.txt
Expand Up @@ -276,6 +276,10 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
Just like linkgit:git-submodule[1], this will detach the
submodules HEAD.

--no-guess::
Do not attempt to create a branch if a remote tracking branch
of the same name exists.

<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
Expand Down
18 changes: 13 additions & 5 deletions builtin/checkout.c
Expand Up @@ -1080,9 +1080,12 @@ static int parse_branchname_arg(int argc, const char **argv,
*/
int recover_with_dwim = dwim_new_local_branch_ok;

if (!has_dash_dash &&
(check_filename(opts->prefix, arg) || !no_wildcard(arg)))
int could_be_checkout_paths = !has_dash_dash &&
check_filename(opts->prefix, arg);

if (!has_dash_dash && !no_wildcard(arg))
recover_with_dwim = 0;

/*
* Accept "git checkout foo" and "git checkout foo --"
* as candidates for dwim.
Expand All @@ -1095,6 +1098,10 @@ static int parse_branchname_arg(int argc, const char **argv,
const char *remote = unique_tracking_name(arg, rev,
dwim_remotes_matched);
if (remote) {
if (could_be_checkout_paths)
die(_("'%s' could be both a local file and a tracking branch.\n"
"Please use -- (and optionally --no-guess) to disambiguate"),
arg);
*new_branch = arg;
arg = remote;
/* DWIMmed to create local branch, case (3).(b) */
Expand Down Expand Up @@ -1229,7 +1236,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
struct checkout_opts opts;
struct branch_info new_branch_info;
char *conflict_style = NULL;
int dwim_new_local_branch = 1;
int dwim_new_local_branch, no_dwim_new_local_branch = 0;
int dwim_remotes_matched = 0;
struct option options[] = {
OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
Expand Down Expand Up @@ -1259,8 +1266,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
N_("do not limit pathspecs to sparse entries only")),
OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
N_("second guess 'git checkout <no-such-branch>'")),
OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
N_("do not second guess 'git checkout <no-such-branch>'")),
OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
N_("do not check if another worktree is holding the given ref")),
{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
Expand All @@ -1283,6 +1290,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);

dwim_new_local_branch = !no_dwim_new_local_branch;
if (opts.show_progress < 0) {
if (opts.quiet)
opts.show_progress = 0;
Expand Down
31 changes: 31 additions & 0 deletions t/t2024-checkout-dwim.sh
Expand Up @@ -278,4 +278,35 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
test_cmp expect actual
'

test_expect_success 'reject when arg could be part of dwim branch' '
git remote add foo file://non-existent-place &&
git update-ref refs/remotes/foo/dwim-arg HEAD &&
echo foo >dwim-arg &&
git add dwim-arg &&
echo bar >dwim-arg &&
test_must_fail git checkout dwim-arg &&
test_must_fail git rev-parse refs/heads/dwim-arg -- &&
grep bar dwim-arg
'

test_expect_success 'disambiguate dwim branch and checkout path (1)' '
git update-ref refs/remotes/foo/dwim-arg1 HEAD &&
echo foo >dwim-arg1 &&
git add dwim-arg1 &&
echo bar >dwim-arg1 &&
git checkout -- dwim-arg1 &&
test_must_fail git rev-parse refs/heads/dwim-arg1 -- &&
grep foo dwim-arg1
'

test_expect_success 'disambiguate dwim branch and checkout path (2)' '
git update-ref refs/remotes/foo/dwim-arg2 HEAD &&
echo foo >dwim-arg2 &&
git add dwim-arg2 &&
echo bar >dwim-arg2 &&
git checkout dwim-arg2 -- &&
git rev-parse refs/heads/dwim-arg2 -- &&
grep bar dwim-arg2
'

test_done
3 changes: 2 additions & 1 deletion t/t9902-completion.sh
Expand Up @@ -1434,7 +1434,8 @@ test_expect_success 'double dash "git checkout"' '
--ignore-other-worktrees Z
--recurse-submodules Z
--progress Z
--no-quiet Z
--guess Z
--no-guess Z
--no-... Z
EOF
'
Expand Down

0 comments on commit 8d7f9db

Please sign in to comment.