Skip to content

Commit 8d7f9db

Browse files
committed
Merge branch 'nd/checkout-dwim-fix'
"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
2 parents 0a84724 + be4908f commit 8d7f9db

File tree

4 files changed

+50
-6
lines changed

4 files changed

+50
-6
lines changed

Documentation/git-checkout.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
276276
Just like linkgit:git-submodule[1], this will detach the
277277
submodules HEAD.
278278

279+
--no-guess::
280+
Do not attempt to create a branch if a remote tracking branch
281+
of the same name exists.
282+
279283
<branch>::
280284
Branch to checkout; if it refers to a branch (i.e., a name that,
281285
when prepended with "refs/heads/", is a valid ref), then that

builtin/checkout.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,9 +1080,12 @@ static int parse_branchname_arg(int argc, const char **argv,
10801080
*/
10811081
int recover_with_dwim = dwim_new_local_branch_ok;
10821082

1083-
if (!has_dash_dash &&
1084-
(check_filename(opts->prefix, arg) || !no_wildcard(arg)))
1083+
int could_be_checkout_paths = !has_dash_dash &&
1084+
check_filename(opts->prefix, arg);
1085+
1086+
if (!has_dash_dash && !no_wildcard(arg))
10851087
recover_with_dwim = 0;
1088+
10861089
/*
10871090
* Accept "git checkout foo" and "git checkout foo --"
10881091
* as candidates for dwim.
@@ -1095,6 +1098,10 @@ static int parse_branchname_arg(int argc, const char **argv,
10951098
const char *remote = unique_tracking_name(arg, rev,
10961099
dwim_remotes_matched);
10971100
if (remote) {
1101+
if (could_be_checkout_paths)
1102+
die(_("'%s' could be both a local file and a tracking branch.\n"
1103+
"Please use -- (and optionally --no-guess) to disambiguate"),
1104+
arg);
10981105
*new_branch = arg;
10991106
arg = remote;
11001107
/* DWIMmed to create local branch, case (3).(b) */
@@ -1229,7 +1236,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
12291236
struct checkout_opts opts;
12301237
struct branch_info new_branch_info;
12311238
char *conflict_style = NULL;
1232-
int dwim_new_local_branch = 1;
1239+
int dwim_new_local_branch, no_dwim_new_local_branch = 0;
12331240
int dwim_remotes_matched = 0;
12341241
struct option options[] = {
12351242
OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
@@ -1259,8 +1266,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
12591266
OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
12601267
OPT_BOOL(0, "ignore-skip-worktree-bits", &opts.ignore_skipworktree,
12611268
N_("do not limit pathspecs to sparse entries only")),
1262-
OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
1263-
N_("second guess 'git checkout <no-such-branch>'")),
1269+
OPT_BOOL(0, "no-guess", &no_dwim_new_local_branch,
1270+
N_("do not second guess 'git checkout <no-such-branch>'")),
12641271
OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
12651272
N_("do not check if another worktree is holding the given ref")),
12661273
{ OPTION_CALLBACK, 0, "recurse-submodules", NULL,
@@ -1283,6 +1290,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
12831290
argc = parse_options(argc, argv, prefix, options, checkout_usage,
12841291
PARSE_OPT_KEEP_DASHDASH);
12851292

1293+
dwim_new_local_branch = !no_dwim_new_local_branch;
12861294
if (opts.show_progress < 0) {
12871295
if (opts.quiet)
12881296
opts.show_progress = 0;

t/t2024-checkout-dwim.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,4 +278,35 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
278278
test_cmp expect actual
279279
'
280280

281+
test_expect_success 'reject when arg could be part of dwim branch' '
282+
git remote add foo file://non-existent-place &&
283+
git update-ref refs/remotes/foo/dwim-arg HEAD &&
284+
echo foo >dwim-arg &&
285+
git add dwim-arg &&
286+
echo bar >dwim-arg &&
287+
test_must_fail git checkout dwim-arg &&
288+
test_must_fail git rev-parse refs/heads/dwim-arg -- &&
289+
grep bar dwim-arg
290+
'
291+
292+
test_expect_success 'disambiguate dwim branch and checkout path (1)' '
293+
git update-ref refs/remotes/foo/dwim-arg1 HEAD &&
294+
echo foo >dwim-arg1 &&
295+
git add dwim-arg1 &&
296+
echo bar >dwim-arg1 &&
297+
git checkout -- dwim-arg1 &&
298+
test_must_fail git rev-parse refs/heads/dwim-arg1 -- &&
299+
grep foo dwim-arg1
300+
'
301+
302+
test_expect_success 'disambiguate dwim branch and checkout path (2)' '
303+
git update-ref refs/remotes/foo/dwim-arg2 HEAD &&
304+
echo foo >dwim-arg2 &&
305+
git add dwim-arg2 &&
306+
echo bar >dwim-arg2 &&
307+
git checkout dwim-arg2 -- &&
308+
git rev-parse refs/heads/dwim-arg2 -- &&
309+
grep bar dwim-arg2
310+
'
311+
281312
test_done

t/t9902-completion.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,8 @@ test_expect_success 'double dash "git checkout"' '
14341434
--ignore-other-worktrees Z
14351435
--recurse-submodules Z
14361436
--progress Z
1437-
--no-quiet Z
1437+
--guess Z
1438+
--no-guess Z
14381439
--no-... Z
14391440
EOF
14401441
'

0 commit comments

Comments
 (0)