Skip to content

Commit ad8d510

Browse files
avargitster
authored andcommitted
checkout: add advice for ambiguous "checkout <branch>"
As the "checkout" documentation describes: If <branch> is not found but there does exist a tracking branch in exactly one remote (call it <remote>) with a matching name, treat as equivalent to [...] <remote>/<branch. This is a really useful feature. The problem is that when you add another remote (e.g. a fork), git won't find a unique branch name anymore, and will instead print this unhelpful message: $ git checkout master error: pathspec 'master' did not match any file(s) known to git Now it will, on my git.git checkout, print: $ ./git --exec-path=$PWD checkout master error: pathspec 'master' did not match any file(s) known to git. hint: 'master' matched more than one remote tracking branch. hint: We found 26 remotes with a reference that matched. So we fell back hint: on trying to resolve the argument as a path, but failed there too! hint: hint: If you meant to check out a remote tracking branch on, e.g. 'origin', hint: you can do so by fully qualifying the name with the --track option: hint: hint: git checkout --track origin/<name> Note that the "error: pathspec[...]" message is still printed. This is because whatever else checkout may have tried earlier, its final fallback is to try to resolve the argument as a path. E.g. in this case: $ ./git --exec-path=$PWD checkout master pu error: pathspec 'master' did not match any file(s) known to git. error: pathspec 'pu' did not match any file(s) known to git. There we don't print the "hint:" implicitly due to earlier logic around the DWIM fallback. That fallback is only used if it looks like we have one argument that might be a branch. I can't think of an intrinsic reason for why we couldn't in some future change skip printing the "error: pathspec[...]" error. However, to do so we'd need to pass something down to checkout_paths() to make it suppress printing an error on its own, and for us to be confident that we're not silencing cases where those errors are meaningful. I don't think that's worth it since determining whether that's the case could easily change due to future changes in the checkout logic. Signed-off-by: Ævar Arnfjörð Bjarmason <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1c55055 commit ad8d510

File tree

5 files changed

+37
-0
lines changed

5 files changed

+37
-0
lines changed

Documentation/config.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,13 @@ advice.*::
344344
Advice shown when you used linkgit:git-checkout[1] to
345345
move to the detach HEAD state, to instruct how to create
346346
a local branch after the fact.
347+
checkoutAmbiguousRemoteBranchName::
348+
Advice shown when the argument to
349+
linkgit:git-checkout[1] ambiguously resolves to a
350+
remote tracking branch on more than one remote in
351+
situations where an unambiguous argument would have
352+
otherwise caused a remote-tracking branch to be
353+
checked out.
347354
amWorkDir::
348355
Advice that shows the location of the patch file when
349356
linkgit:git-am[1] fails to apply it.

advice.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ int advice_add_embedded_repo = 1;
2121
int advice_ignored_hook = 1;
2222
int advice_waiting_for_editor = 1;
2323
int advice_graft_file_deprecated = 1;
24+
int advice_checkout_ambiguous_remote_branch_name = 1;
2425

2526
static int advice_use_color = -1;
2627
static char advice_colors[][COLOR_MAXLEN] = {
@@ -72,6 +73,7 @@ static struct {
7273
{ "ignoredhook", &advice_ignored_hook },
7374
{ "waitingforeditor", &advice_waiting_for_editor },
7475
{ "graftfiledeprecated", &advice_graft_file_deprecated },
76+
{ "checkoutambiguousremotebranchname", &advice_checkout_ambiguous_remote_branch_name },
7577

7678
/* make this an alias for backward compatibility */
7779
{ "pushnonfastforward", &advice_push_update_rejected }

advice.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extern int advice_add_embedded_repo;
2222
extern int advice_ignored_hook;
2323
extern int advice_waiting_for_editor;
2424
extern int advice_graft_file_deprecated;
25+
extern int advice_checkout_ambiguous_remote_branch_name;
2526

2627
int git_default_advice_config(const char *var, const char *value);
2728
__attribute__((format (printf, 1, 2)))

builtin/checkout.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "resolve-undo.h"
2323
#include "submodule-config.h"
2424
#include "submodule.h"
25+
#include "advice.h"
2526

2627
static const char * const checkout_usage[] = {
2728
N_("git checkout [<options>] <branch>"),
@@ -1267,6 +1268,18 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
12671268
UNLEAK(opts);
12681269
if (opts.patch_mode || opts.pathspec.nr) {
12691270
int ret = checkout_paths(&opts, new_branch_info.name);
1271+
if (ret && dwim_remotes_matched > 1 &&
1272+
advice_checkout_ambiguous_remote_branch_name)
1273+
advise(_("'%s' matched more than one remote tracking branch.\n"
1274+
"We found %d remotes with a reference that matched. So we fell back\n"
1275+
"on trying to resolve the argument as a path, but failed there too!\n"
1276+
"\n"
1277+
"If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
1278+
"you can do so by fully qualifying the name with the --track option:\n"
1279+
"\n"
1280+
" git checkout --track origin/<name>"),
1281+
argv[0],
1282+
dwim_remotes_matched);
12701283
return ret;
12711284
} else {
12721285
return checkout_branch(&opts, &new_branch_info);

t/t2024-checkout-dwim.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,20 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '
7676
test_branch master
7777
'
7878

79+
test_expect_success 'checkout of branch from multiple remotes fails with advice' '
80+
git checkout -B master &&
81+
test_might_fail git branch -D foo &&
82+
test_must_fail git checkout foo 2>stderr &&
83+
test_branch master &&
84+
status_uno_is_clean &&
85+
test_i18ngrep "^hint: " stderr &&
86+
test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
87+
checkout foo 2>stderr &&
88+
test_branch master &&
89+
status_uno_is_clean &&
90+
test_i18ngrep ! "^hint: " stderr
91+
'
92+
7993
test_expect_success 'checkout of branch from a single remote succeeds #1' '
8094
git checkout -B master &&
8195
test_might_fail git branch -D bar &&

0 commit comments

Comments
 (0)