checkout: disambiguate dwim tracking branches and local files
When checkout dwim is added in [1], it is restricted to only dwim when certain conditions are met and fall back to default checkout behavior otherwise. It turns out falling back could be confusing. One of the conditions to turn git checkout frotz to git checkout -b frotz origin/frotz is that frotz must not exist as a file. But when the user comes to expect "git checkout frotz" to create the branch "frotz" and there happens to be a file named "frotz", git's silently reverting "frotz" file content is not helping. This is reported in Git mailing list [2] and even used as an example of "Git is bad" elsewhere [3]. We normally try to do the right thing, but when there are multiple "right things" to do, it's best to leave it to the user to decide. Check this case, ask the user to to disambiguate: - "git checkout -- foo" will check out path "foo" - "git checkout foo --" will dwim and create branch "foo" [4] For users who do not want dwim, use --no-guess. It's useless in this particular case because "git checkout --no-guess foo --" will just fail. But it could be used by scripts. [1]70c9ac2f19
(DWIM "git checkout frotz" to "git checkout -b frotz origin/frotz" - 2009-10-18) [2] https://public-inbox.org/git/CACsJy8B2TVr1g+k+eSQ=pBEO3WN4_LtgLo9gpur8X7Z9GOFL_A@mail.gmail.com/ [3] https://news.ycombinator.com/item?id=18230655 [4]a047fafc78
(checkout: allow dwim for branch creation for "git checkout $branch --" - 2013-10-18) Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
d166e6afe5
commit
be4908f103
@ -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
|
||||
|
@ -1079,9 +1079,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.
|
||||
@ -1094,6 +1097,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) */
|
||||
@ -1228,7 +1235,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")),
|
||||
@ -1258,8 +1265,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,
|
||||
@ -1282,6 +1289,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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
'
|
||||
|
Loading…
Reference in New Issue
Block a user