git reset and similar commands use -- to disambiguate between revisions and paths on the command line. The same syntax is not necessary to specify a revision to git checkout (for convenience and historical reasons, revisions are preferred over paths), but for consistency it is accepted: git checkout master --; # check out master branch, not "master" file. The autovivification of branches introduced by 70c9ac2f1 (DWIM "git checkout frotz" to "git checkout -b frotz origin/frotz", 2009-10-18) is currently disabled by that syntax, for no good reason. Paranoid scripts can still use git checkout --no-guess master or even better, old=$(git rev-parse --verify HEAD) new=$(git rev-parse --verify refs/heads/master^0) git read-tree -m -u --exclude-standard $old $new git symbolic-ref -m "$me: switching branches" HEAD refs/heads/master Requested-by: Dun Peal <dunpealer@xxxxxxxxx> Signed-off-by: Jonathan Nieder <jrnieder@xxxxxxxxx> --- Junio C Hamano wrote: > That looks simpler than what I just did. Can we have a few tests, too? Sure. The tests are not too heavy, though (e.g., no "ambiguous ref" stuff). >> - !check_filename(NULL, arg) && >> + (has_dash_dash || !check_filename(NULL, arg)) && >> argc == 1) { Should have been (argc == 2) in the has_dash_dash case. builtin/checkout.c | 19 ++++++--- t/t2010-checkout-ambiguous.sh | 83 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/builtin/checkout.c b/builtin/checkout.c index 9240faf..6c3de9f 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -771,6 +771,12 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) * <ref> must be a valid tree, everything after the '--' must be * a path. * + * Except: with no paths, if <something> does not resolve as + * an object, no -t nor -b was given, and there is a tracking + * branch whose name is <something> in one and only one remote, + * then this is a short-hand to fork local <something> from + * that remote-tracking branch. + * * case 2: git checkout -- [<paths>] * * everything after the '--' must be paths. @@ -808,23 +814,24 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) arg = "@{-1}"; if (get_sha1_mb(arg, rev)) { - if (has_dash_dash) /* case (1) */ - die("invalid reference: %s", arg); + trace_printf("trace: guess = %d\n", dwim_new_local_branch); if (!patch_mode && dwim_new_local_branch && opts.track == BRANCH_TRACK_UNSPECIFIED && !opts.new_branch && - !check_filename(NULL, arg) && - argc == 1) { + (has_dash_dash || !check_filename(NULL, arg)) && + (argc == has_dash_dash ? 2 : 1)) { const char *remote = unique_tracking_name(arg); if (!remote || get_sha1(remote, rev)) goto no_reference; opts.new_branch = arg; arg = remote; /* DWIMmed to create local branch */ - } - else + } else if (has_dash_dash) { /* case (1) */ + die("invalid reference: %s", arg); + } else { goto no_reference; + } } /* we can't end up being in (2) anymore, eat the argument */ diff --git a/t/t2010-checkout-ambiguous.sh b/t/t2010-checkout-ambiguous.sh index 7cc0a35..80ac7b5 100755 --- a/t/t2010-checkout-ambiguous.sh +++ b/t/t2010-checkout-ambiguous.sh @@ -4,10 +4,25 @@ test_description='checkout and pathspecs/refspecs ambiguities' . ./test-lib.sh +test_branch_is () { + echo "refs/heads/$1" >expect && + git symbolic-ref HEAD >actual && + test_cmp expect actual +} + test_expect_success 'setup' ' + git init upstream && + ( + cd upstream && + test_commit upstream-commit && + git branch -m upstream-topic + ) && + git remote add upstream upstream && + git fetch upstream && echo hello >world && echo hello >all && git add all world && + test_tick && git commit -m initial && git branch world ' @@ -32,6 +47,74 @@ test_expect_success 'non ambiguous call' ' git checkout all ' +test_expect_success "autovivification (Dscho's DWIM)" ' + git checkout master && + test_might_fail git branch -D upstream-topic && + git checkout upstream-topic && + git diff --exit-code upstream-topic upstream/upstream-topic && + test_branch_is upstream-topic +' + +test_expect_success 'autovivification with --' ' + git checkout master && + test_might_fail git branch -D upstream-topic && + git checkout upstream-topic -- && + git diff --exit-code upstream-topic upstream/upstream-topic && + test_branch_is upstream-topic +' + +test_expect_success 'no autovivification after --' ' + git checkout master && + test_might_fail git branch -D upstream-topic && + test_must_fail git checkout -- upstream-topic && + test_branch_is master +' + +test_expect_success '--no-guess defeats autovivification' ' + git checkout master && + test_might_fail git branch -D upstream-topic && + test_must_fail git checkout --no-guess upstream-topic && + test_must_fail git checkout --no-guess upstream-topic -- && + test_branch_is master +' + +test_expect_success 'paths defeat autovivification' ' + git checkout master && + test_might_fail git branch -D upstream-topic && + test_must_fail git checkout upstream-topic -- . && + test_must_fail git checkout upstream-topic . && + test_branch_is master +' + +test_expect_success 'real branch defeats autovivification' ' + git checkout master && + git update-ref refs/heads/upstream-topic HEAD^0 && + git checkout upstream-topic && + test_must_fail git diff --exit-code upstream-topic upstream/upstream-topic && + test_branch_is upstream-topic +' + +test_expect_success '-t defeats autovivification' ' + git checkout master && + test_might_fail git branch -D upstream-topic && + test_must_fail git checkout -t upstream-topic && + test_branch_is master +' + +test_expect_success 'checkout of remote-tracking branch detaches HEAD' ' + git checkout master && + test_might_fail git branch -D upstream-topic && + git checkout upstream/upstream-topic && + test_must_fail git symbolic-ref HEAD +' + +test_expect_success 'checkout -t of remote-tracking branch does not detach HEAD' ' + git checkout master && + test_might_fail git branch -D upstream-topic && + git checkout -t upstream/upstream-topic && + test_branch_is upstream-topic +' + test_expect_success 'allow the most common case' ' git checkout world && test "refs/heads/world" = "$(git symbolic-ref HEAD)" -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html