Merge branch 'nd/checkout-disambiguation'

"git checkout <word>" does not follow the usual disambiguation
rules when the <word> can be both a rev and a path, to allow
checking out a branch 'foo' in a project that happens to have a
file 'foo' in the working tree without having to disambiguate.
This was poorly documented and the check was incorrect when the
command was run from a subdirectory.

* nd/checkout-disambiguation:
  checkout: fix ambiguity check in subdir
  checkout.txt: document a common case that ignores ambiguation rules
  checkout: add some spaces between code and comment
This commit is contained in:
Junio C Hamano
2016-09-26 16:09:18 -07:00
4 changed files with 36 additions and 3 deletions

View File

@ -419,6 +419,18 @@ $ git reflog -2 HEAD # or
$ git log -g -2 HEAD $ git log -g -2 HEAD
------------ ------------
ARGUMENT DISAMBIGUATION
-----------------------
When there is only one argument given and it is not `--` (e.g. "git
checkout abc"), and when the argument is both a valid `<tree-ish>`
(e.g. a branch "abc" exists) and a valid `<pathspec>` (e.g. a file
or a directory whose name is "abc" exists), Git would usually ask
you to disambiguate. Because checking out a branch is so common an
operation, however, "git checkout abc" takes "abc" as a `<tree-ish>`
in such a situation. Use `git checkout -- <pathspec>` if you want
to checkout these paths out of the index.
EXAMPLES EXAMPLES
-------- --------

View File

@ -985,7 +985,7 @@ static int parse_branchname_arg(int argc, const char **argv,
int recover_with_dwim = dwim_new_local_branch_ok; int recover_with_dwim = dwim_new_local_branch_ok;
if (!has_dash_dash && if (!has_dash_dash &&
(check_filename(NULL, arg) || !no_wildcard(arg))) (check_filename(opts->prefix, arg) || !no_wildcard(arg)))
recover_with_dwim = 0; recover_with_dwim = 0;
/* /*
* Accept "git checkout foo" and "git checkout foo --" * Accept "git checkout foo" and "git checkout foo --"
@ -1038,7 +1038,7 @@ static int parse_branchname_arg(int argc, const char **argv,
if (!*source_tree) /* case (1): want a tree */ if (!*source_tree) /* case (1): want a tree */
die(_("reference is not a tree: %s"), arg); die(_("reference is not a tree: %s"), arg);
if (!has_dash_dash) {/* case (3).(d) -> (1) */ if (!has_dash_dash) { /* case (3).(d) -> (1) */
/* /*
* Do not complain the most common case * Do not complain the most common case
* git checkout branch * git checkout branch
@ -1046,7 +1046,7 @@ static int parse_branchname_arg(int argc, const char **argv,
* it would be extremely annoying. * it would be extremely annoying.
*/ */
if (argc) if (argc)
verify_non_filename(NULL, arg); verify_non_filename(opts->prefix, arg);
} else { } else {
argcount++; argcount++;
argv++; argv++;

View File

@ -41,6 +41,15 @@ test_expect_success 'check ambiguity' '
test_must_fail git checkout world all test_must_fail git checkout world all
' '
test_expect_success 'check ambiguity in subdir' '
mkdir sub &&
# not ambiguous because sub/world does not exist
git -C sub checkout world ../all &&
echo hello >sub/world &&
# ambiguous because sub/world does exist
test_must_fail git -C sub checkout world ../all
'
test_expect_success 'disambiguate checking out from a tree-ish' ' test_expect_success 'disambiguate checking out from a tree-ish' '
echo bye > world && echo bye > world &&
git checkout world -- world && git checkout world -- world &&

View File

@ -174,6 +174,18 @@ test_expect_success 'checkout of branch with a file having the same name fails'
test_branch master test_branch master
' '
test_expect_success 'checkout of branch with a file in subdir having the same name fails' '
git checkout -B master &&
test_might_fail git branch -D spam &&
>spam &&
mkdir sub &&
mv spam sub/spam &&
test_must_fail git -C sub checkout spam &&
test_must_fail git rev-parse --verify refs/heads/spam &&
test_branch master
'
test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' ' test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
git checkout -B master && git checkout -B master &&
test_might_fail git branch -D spam && test_might_fail git branch -D spam &&