* [PATCH 0/2] Improving advise messages from "switch"
@ 2026-01-27 19:29 Junio C Hamano
2026-01-27 19:29 ` [PATCH 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Junio C Hamano @ 2026-01-27 19:29 UTC (permalink / raw)
To: git; +Cc: Simon Cheng
Simon Cheng noticed [*] that "git switch <name>", in an attempt to
create a local branch <name> after a remote tracking branch of the
same name, fails correctly when multiple remotes have branches with
that name, but gives an advise message to disambiguate using "git
checkout".
Obviously, the user wanted to use "git switch", and "git switch" is
equipped to do so, just like "git checkout" can, so it makes more
sense to give hint based on "git switch".
The first patch of this series restructures the call chain of
checkout, switch, and restore commands a bit, so that a helper deep
in the callchain can find out what end-user command it is working
for. The second patch takes advantage of the new structure to tweak
the advise message in question, given from a helper function in a
fairly deep part of the call chain.
[Reference]
* https://lore.kernel.org/git/CA+itcS0iyqNyzOP0cueLg7B3yadoEr_VWJ-QoL+YPFUPJiE2RQ@mail.gmail.com
Junio C Hamano (2):
checkout: pass program-readable token to unified "main"
checkout: tell "parse_remote_branch" which command is calling it
builtin/checkout.c | 92 ++++++++++++++++++++++++++++-----------
t/t2027-checkout-track.sh | 21 +++++++++
2 files changed, 88 insertions(+), 25 deletions(-)
--
2.53.0-rc2-135-gb1217c0133
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/2] checkout: pass program-readable token to unified "main"
2026-01-27 19:29 [PATCH 0/2] Improving advise messages from "switch" Junio C Hamano
@ 2026-01-27 19:29 ` Junio C Hamano
2026-01-27 19:29 ` [PATCH 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
2026-01-29 19:06 ` [PATCH v2 0/2] Improving advise messages from "switch" Junio C Hamano
2 siblings, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2026-01-27 19:29 UTC (permalink / raw)
To: git; +Cc: Simon Cheng
The "git checkout", "git switch", and "git restore" commands share a
single implementation, checkout_main(), which switches error message
it gives using the usage string passed by each of these three
front-ends.
In order to be able to tweak behaviours of the commands based on
which one we are executing, invent an enum that denotes which one of
these three commands is currently executing, and pass that to
checkout_main() instead. With this step, there is no externally
visible behaviour change, as this enum parameter is only used to
choose among the three usage strings.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/checkout.c | 63 +++++++++++++++++++++++++++++++---------------
1 file changed, 43 insertions(+), 20 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe..4f189fde48 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -43,22 +43,6 @@
#include "parallel-checkout.h"
#include "add-interactive.h"
-static const char * const checkout_usage[] = {
- N_("git checkout [<options>] <branch>"),
- N_("git checkout [<options>] [<branch>] -- <file>..."),
- NULL,
-};
-
-static const char * const switch_branch_usage[] = {
- N_("git switch [<options>] [<branch>]"),
- NULL,
-};
-
-static const char * const restore_usage[] = {
- N_("git restore [<options>] [--source=<branch>] <file>..."),
- NULL,
-};
-
struct checkout_opts {
int patch_mode;
int patch_context;
@@ -1293,6 +1277,13 @@ static void setup_new_branch_info_and_source_tree(
}
}
+
+enum checkout_command {
+ CHECKOUT_CHECKOUT = 1,
+ CHECKOUT_SWITCH = 2,
+ CHECKOUT_RESTORE = 3,
+};
+
static char *parse_remote_branch(const char *arg,
struct object_id *rev,
int could_be_checkout_paths)
@@ -1767,12 +1758,44 @@ static char cb_option = 'b';
static int checkout_main(int argc, const char **argv, const char *prefix,
struct checkout_opts *opts, struct option *options,
- const char * const usagestr[])
+ enum checkout_command which_command)
{
int parseopt_flags = 0;
struct branch_info new_branch_info = { 0 };
int ret;
+ static const char * const checkout_usage[] = {
+ N_("git checkout [<options>] <branch>"),
+ N_("git checkout [<options>] [<branch>] -- <file>..."),
+ NULL,
+ };
+
+ static const char * const switch_branch_usage[] = {
+ N_("git switch [<options>] [<branch>]"),
+ NULL,
+ };
+
+ static const char * const restore_usage[] = {
+ N_("git restore [<options>] [--source=<branch>] <file>..."),
+ NULL,
+ };
+
+ const char * const *usagestr;
+
+ switch (which_command) {
+ case CHECKOUT_CHECKOUT:
+ usagestr = checkout_usage;
+ break;
+ case CHECKOUT_SWITCH:
+ usagestr = switch_branch_usage;
+ break;
+ case CHECKOUT_RESTORE:
+ usagestr = restore_usage;
+ break;
+ default:
+ BUG("No such checkout variant %d", which_command);
+ }
+
opts->overwrite_ignore = 1;
opts->prefix = prefix;
opts->show_progress = -1;
@@ -2032,7 +2055,7 @@ int cmd_checkout(int argc,
options = add_checkout_path_options(&opts, options);
return checkout_main(argc, argv, prefix, &opts, options,
- checkout_usage);
+ CHECKOUT_CHECKOUT);
}
int cmd_switch(int argc,
@@ -2071,7 +2094,7 @@ int cmd_switch(int argc,
cb_option = 'c';
return checkout_main(argc, argv, prefix, &opts, options,
- switch_branch_usage);
+ CHECKOUT_SWITCH);
}
int cmd_restore(int argc,
@@ -2107,5 +2130,5 @@ int cmd_restore(int argc,
options = add_checkout_path_options(&opts, options);
return checkout_main(argc, argv, prefix, &opts, options,
- restore_usage);
+ CHECKOUT_RESTORE);
}
--
2.53.0-rc2-135-gb1217c0133
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/2] checkout: tell "parse_remote_branch" which command is calling it
2026-01-27 19:29 [PATCH 0/2] Improving advise messages from "switch" Junio C Hamano
2026-01-27 19:29 ` [PATCH 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
@ 2026-01-27 19:29 ` Junio C Hamano
2026-01-27 20:35 ` Kristoffer Haugsbakk
2026-01-29 19:06 ` [PATCH v2 0/2] Improving advise messages from "switch" Junio C Hamano
2 siblings, 1 reply; 11+ messages in thread
From: Junio C Hamano @ 2026-01-27 19:29 UTC (permalink / raw)
To: git; +Cc: Simon Cheng
When "git checkout <dwim>" and "git switch <dwim>" need to error out
due to ambiguity of the branch name <dwim>, these command give an
advise message that tells a sample command to show how to
disambiguate from the parse_remote_branch() function. The sample
command hardcodes "git checkout", since this feature predates "git
switch" by a large margin. To a user who said "git switch <dwim>"
and got this message, it is confusing.
Pass the "enum checkout_command", which was invented in the previous
step for this exact purpose, down the call chain leading to
parse_remote_branch() function to change the sample command shown to
the user in this advise message.
Reported-by: Simon Cheng <cyqsimon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/checkout.c | 29 ++++++++++++++++++++++++-----
t/t2027-checkout-track.sh | 21 +++++++++++++++++++++
2 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4f189fde48..17f31c30b2 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1286,7 +1286,8 @@ enum checkout_command {
static char *parse_remote_branch(const char *arg,
struct object_id *rev,
- int could_be_checkout_paths)
+ int could_be_checkout_paths,
+ enum checkout_command which_command)
{
int num_matches = 0;
char *remote = unique_tracking_name(arg, rev, &num_matches);
@@ -1299,14 +1300,30 @@ static char *parse_remote_branch(const char *arg,
if (!remote && num_matches > 1) {
if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {
+ const char *cmdname;
+
+ switch (which_command) {
+ case CHECKOUT_CHECKOUT:
+ cmdname = "checkout";
+ break;
+ case CHECKOUT_SWITCH:
+ cmdname = "switch";
+ break;
+ default:
+ BUG("command <%d> should not reach parse_remote_branch",
+ which_command);
+ break;
+ }
+
advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
"you can do so by fully qualifying the name with the --track option:\n"
"\n"
- " git checkout --track origin/<name>\n"
+ " git %s --track origin/<name>\n"
"\n"
"If you'd like to always have checkouts of an ambiguous <name> prefer\n"
"one remote, e.g. the 'origin' remote, consider setting\n"
- "checkout.defaultRemote=origin in your config."));
+ "checkout.defaultRemote=origin in your config."),
+ cmdname);
}
die(_("'%s' matched multiple (%d) remote tracking branches"),
@@ -1318,6 +1335,7 @@ static char *parse_remote_branch(const char *arg,
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
+ enum checkout_command which_command,
struct branch_info *new_branch_info,
struct checkout_opts *opts,
struct object_id *rev)
@@ -1427,7 +1445,8 @@ static int parse_branchname_arg(int argc, const char **argv,
if (recover_with_dwim) {
remote = parse_remote_branch(arg, rev,
- could_be_checkout_paths);
+ could_be_checkout_paths,
+ which_command);
if (remote) {
*new_branch = arg;
arg = remote;
@@ -1916,7 +1935,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
opts->dwim_new_local_branch &&
opts->track == BRANCH_TRACK_UNSPECIFIED &&
!opts->new_branch;
- int n = parse_branchname_arg(argc, argv, dwim_ok,
+ int n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
&new_branch_info, opts, &rev);
argv += n;
argc -= n;
diff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh
index a397790df5..e9f8d8ec48 100755
--- a/t/t2027-checkout-track.sh
+++ b/t/t2027-checkout-track.sh
@@ -47,4 +47,25 @@ test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
test_cmp_config refs/heads/main branch.b4.merge
'
+test_expect_success 'ambiguous tracking info' '
+ # Set up a few remote repositories
+ git init --bare --initial-branch=trunk src1 &&
+ git init --bare --initial-branch=trunk src2 &&
+ git push src1 one:refs/heads/trunk &&
+ git push src2 two:refs/heads/trunk &&
+
+ git remote add -f src1 "file://$PWD/src1" &&
+ git remote add -f src2 "file://$PWD/src2" &&
+
+ # DWIM
+ test_must_fail git checkout trunk 2>hint &&
+ test_grep "hint: *git checkout --track" hint &&
+ test_grep ! "hint: *git switch --track" hint &&
+
+ { git update-ref -d refs/heads/trunk || :; } &&
+ test_must_fail git switch trunk 2>hint &&
+ test_grep ! "hint: *git checkout --track" hint &&
+ test_grep "hint: *git switch --track" hint
+'
+
test_done
--
2.53.0-rc2-135-gb1217c0133
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] checkout: tell "parse_remote_branch" which command is calling it
2026-01-27 19:29 ` [PATCH 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
@ 2026-01-27 20:35 ` Kristoffer Haugsbakk
2026-01-27 21:22 ` Junio C Hamano
0 siblings, 1 reply; 11+ messages in thread
From: Kristoffer Haugsbakk @ 2026-01-27 20:35 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Simon Cheng
On Tue, Jan 27, 2026, at 20:29, Junio C Hamano wrote:
> When "git checkout <dwim>" and "git switch <dwim>" need to error out
> due to ambiguity of the branch name <dwim>, these command give an
s/these command/these two commands/ or something.
> advise message that tells a sample command to show how to
> disambiguate from the parse_remote_branch() function. The sample
> command hardcodes "git checkout", since this feature predates "git
> switch" by a large margin. To a user who said "git switch <dwim>"
> and got this message, it is confusing.
>
> Pass the "enum checkout_command", which was invented in the previous
> step for this exact purpose, down the call chain leading to
> parse_remote_branch() function to change the sample command shown to
> the user in this advise message.
The commit message could also say that it adds a regression test for
this advice (for the first time).
>
> Reported-by: Simon Cheng <cyqsimon@gmail.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> builtin/checkout.c | 29 ++++++++++++++++++++++++-----
> t/t2027-checkout-track.sh | 21 +++++++++++++++++++++
> 2 files changed, 45 insertions(+), 5 deletions(-)
>[snip]
> diff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh
> index a397790df5..e9f8d8ec48 100755
> --- a/t/t2027-checkout-track.sh
> +++ b/t/t2027-checkout-track.sh
> @@ -47,4 +47,25 @@ test_expect_success 'checkout --track -b overrides
> autoSetupMerge=inherit' '
> test_cmp_config refs/heads/main branch.b4.merge
> '
>
> +test_expect_success 'ambiguous tracking info' '
> + # Set up a few remote repositories
> + git init --bare --initial-branch=trunk src1 &&
> + git init --bare --initial-branch=trunk src2 &&
> + git push src1 one:refs/heads/trunk &&
> + git push src2 two:refs/heads/trunk &&
> +
> + git remote add -f src1 "file://$PWD/src1" &&
> + git remote add -f src2 "file://$PWD/src2" &&
> +
> + # DWIM
> + test_must_fail git checkout trunk 2>hint &&
> + test_grep "hint: *git checkout --track" hint &&
> + test_grep ! "hint: *git switch --track" hint &&
> +
> + { git update-ref -d refs/heads/trunk || :; } &&
I don’t understand what the purpose of this is after `git checkout` but
before `git switch`. I can delete it and the test still passes. Is it
post-test cleanup?
> + test_must_fail git switch trunk 2>hint &&
> + test_grep ! "hint: *git checkout --track" hint &&
> + test_grep "hint: *git switch --track" hint
> +'
Maybe just the positive greps are enough. I read these a few times
because I thought the order was wrong, i.e. that `hint` was overwritten
before it got tested. The regression that they test are unlikely and
these negative greps might not make immediate sense for future
readers. I dunno.
I was about to suggest parameterizing the command but that makes it
harder to grep for that part of the advice.
checkout_with_cmd () {
cmd="$1"
test_must_fail git $cmd trunk 2>hint &&
test_grep --fixed-string \
"hint: git $cmd --track origin/<name>" hint
}
> +
> test_done
> --
> 2.53.0-rc2-135-gb1217c0133
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2] checkout: tell "parse_remote_branch" which command is calling it
2026-01-27 20:35 ` Kristoffer Haugsbakk
@ 2026-01-27 21:22 ` Junio C Hamano
0 siblings, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2026-01-27 21:22 UTC (permalink / raw)
To: Kristoffer Haugsbakk; +Cc: git, Simon Cheng
"Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com> writes:
>> + # DWIM
>> + test_must_fail git checkout trunk 2>hint &&
>> + test_grep "hint: *git checkout --track" hint &&
>> + test_grep ! "hint: *git switch --track" hint &&
>> +
>> + { git update-ref -d refs/heads/trunk || :; } &&
>
> I don’t understand what the purpose of this is after `git checkout` but
> before `git switch`. I can delete it and the test still passes. Is it
> post-test cleanup?
Just in case "git checkout trunk" that was expected to fail still
creates the 'trunk' branch by a bug. I do not want the failure of
the next "git switch trunk" to be due to "hey, you already have a
local branch of that name", and make sure the failure is from "you
have two remotes with trunk, and I cannot tell which one you meant".
>> + test_must_fail git switch trunk 2>hint &&
>> + test_grep ! "hint: *git checkout --track" hint &&
>> + test_grep "hint: *git switch --track" hint
>> +'
>
> Maybe just the positive greps are enough. I read these a few times
> because I thought the order was wrong, i.e. that `hint` was overwritten
> before it got tested. The regression that they test are unlikely and
> these negative greps might not make immediate sense for future
> readers. I dunno.
Possibly. These tests to expect concrete strings in the output are
already familiar with how these output strings are built, so they
should know that when 'git checkout --track' appears, it is very
unlikely that 'git switch --track' would appear there, for example.
Thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 0/2] Improving advise messages from "switch"
2026-01-27 19:29 [PATCH 0/2] Improving advise messages from "switch" Junio C Hamano
2026-01-27 19:29 ` [PATCH 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
2026-01-27 19:29 ` [PATCH 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
@ 2026-01-29 19:06 ` Junio C Hamano
2026-01-29 19:06 ` [PATCH v2 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
2026-01-29 19:06 ` [PATCH v2 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
2 siblings, 2 replies; 11+ messages in thread
From: Junio C Hamano @ 2026-01-29 19:06 UTC (permalink / raw)
To: git; +Cc: Simon Cheng, Kristoffer Haugsbakk
Simon Cheng noticed [*] that "git switch <name>", in an attempt to
create a local branch <name> after a remote tracking branch of the
same name, fails correctly when multiple remotes have branches with
that name, but gives an advise message to disambiguate using "git
checkout".
Obviously, the user wanted to use "git switch", and "git switch" is
equipped to do so, just like "git checkout" can, so it makes more
sense to give hint based on "git switch".
The first patch of this series restructures the call chain of
checkout, switch, and restore commands a bit, so that a helper deep
in the callchain can find out what end-user command it is working
for. The second patch takes advantage of the new structure to tweak
the advise message in question, given from a helper function in a
fairly deep part of the call chain.
[Reference]
* https://lore.kernel.org/git/CA+itcS0iyqNyzOP0cueLg7B3yadoEr_VWJ-QoL+YPFUPJiE2RQ@mail.gmail.com
Junio C Hamano (2):
checkout: pass program-readable token to unified "main"
checkout: tell "parse_remote_branch" which command is calling it
builtin/checkout.c | 92 ++++++++++++++++++++++++++++-----------
t/t2027-checkout-track.sh | 18 ++++++++
2 files changed, 85 insertions(+), 25 deletions(-)
---
This second iteration simplifies the tests in the second patch
somewhat, and rewords its proposed log message, as suggested by
Kristoffer Haugsbakk.
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 1/2] checkout: pass program-readable token to unified "main"
2026-01-29 19:06 ` [PATCH v2 0/2] Improving advise messages from "switch" Junio C Hamano
@ 2026-01-29 19:06 ` Junio C Hamano
2026-02-06 16:05 ` Patrick Steinhardt
2026-01-29 19:06 ` [PATCH v2 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
1 sibling, 1 reply; 11+ messages in thread
From: Junio C Hamano @ 2026-01-29 19:06 UTC (permalink / raw)
To: git; +Cc: Simon Cheng, Kristoffer Haugsbakk
The "git checkout", "git switch", and "git restore" commands share a
single implementation, checkout_main(), which switches error message
it gives using the usage string passed by each of these three
front-ends.
In order to be able to tweak behaviours of the commands based on
which one we are executing, invent an enum that denotes which one of
these three commands is currently executing, and pass that to
checkout_main() instead. With this step, there is no externally
visible behaviour change, as this enum parameter is only used to
choose among the three usage strings.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
* No change relative to v1
builtin/checkout.c | 63 +++++++++++++++++++++++++++++++---------------
1 file changed, 43 insertions(+), 20 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index f9453473fe..4f189fde48 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -43,22 +43,6 @@
#include "parallel-checkout.h"
#include "add-interactive.h"
-static const char * const checkout_usage[] = {
- N_("git checkout [<options>] <branch>"),
- N_("git checkout [<options>] [<branch>] -- <file>..."),
- NULL,
-};
-
-static const char * const switch_branch_usage[] = {
- N_("git switch [<options>] [<branch>]"),
- NULL,
-};
-
-static const char * const restore_usage[] = {
- N_("git restore [<options>] [--source=<branch>] <file>..."),
- NULL,
-};
-
struct checkout_opts {
int patch_mode;
int patch_context;
@@ -1293,6 +1277,13 @@ static void setup_new_branch_info_and_source_tree(
}
}
+
+enum checkout_command {
+ CHECKOUT_CHECKOUT = 1,
+ CHECKOUT_SWITCH = 2,
+ CHECKOUT_RESTORE = 3,
+};
+
static char *parse_remote_branch(const char *arg,
struct object_id *rev,
int could_be_checkout_paths)
@@ -1767,12 +1758,44 @@ static char cb_option = 'b';
static int checkout_main(int argc, const char **argv, const char *prefix,
struct checkout_opts *opts, struct option *options,
- const char * const usagestr[])
+ enum checkout_command which_command)
{
int parseopt_flags = 0;
struct branch_info new_branch_info = { 0 };
int ret;
+ static const char * const checkout_usage[] = {
+ N_("git checkout [<options>] <branch>"),
+ N_("git checkout [<options>] [<branch>] -- <file>..."),
+ NULL,
+ };
+
+ static const char * const switch_branch_usage[] = {
+ N_("git switch [<options>] [<branch>]"),
+ NULL,
+ };
+
+ static const char * const restore_usage[] = {
+ N_("git restore [<options>] [--source=<branch>] <file>..."),
+ NULL,
+ };
+
+ const char * const *usagestr;
+
+ switch (which_command) {
+ case CHECKOUT_CHECKOUT:
+ usagestr = checkout_usage;
+ break;
+ case CHECKOUT_SWITCH:
+ usagestr = switch_branch_usage;
+ break;
+ case CHECKOUT_RESTORE:
+ usagestr = restore_usage;
+ break;
+ default:
+ BUG("No such checkout variant %d", which_command);
+ }
+
opts->overwrite_ignore = 1;
opts->prefix = prefix;
opts->show_progress = -1;
@@ -2032,7 +2055,7 @@ int cmd_checkout(int argc,
options = add_checkout_path_options(&opts, options);
return checkout_main(argc, argv, prefix, &opts, options,
- checkout_usage);
+ CHECKOUT_CHECKOUT);
}
int cmd_switch(int argc,
@@ -2071,7 +2094,7 @@ int cmd_switch(int argc,
cb_option = 'c';
return checkout_main(argc, argv, prefix, &opts, options,
- switch_branch_usage);
+ CHECKOUT_SWITCH);
}
int cmd_restore(int argc,
@@ -2107,5 +2130,5 @@ int cmd_restore(int argc,
options = add_checkout_path_options(&opts, options);
return checkout_main(argc, argv, prefix, &opts, options,
- restore_usage);
+ CHECKOUT_RESTORE);
}
--
2.53.0-rc2-135-gb1217c0133
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v2 2/2] checkout: tell "parse_remote_branch" which command is calling it
2026-01-29 19:06 ` [PATCH v2 0/2] Improving advise messages from "switch" Junio C Hamano
2026-01-29 19:06 ` [PATCH v2 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
@ 2026-01-29 19:06 ` Junio C Hamano
2026-02-06 16:05 ` Patrick Steinhardt
1 sibling, 1 reply; 11+ messages in thread
From: Junio C Hamano @ 2026-01-29 19:06 UTC (permalink / raw)
To: git; +Cc: Simon Cheng, Kristoffer Haugsbakk
When "git checkout <dwim>" and "git switch <dwim>" need to error out
due to ambiguity of the branch name <dwim>, these two commands give
an advise message with a sample command that tells the user how to
disambiguate from the parse_remote_branch() function. The sample
command hardcodes "git checkout", since this feature predates "git
switch" by a large margin. To a user who said "git switch <dwim>"
and got this message, it is confusing.
Pass the "enum checkout_command", which was invented in the previous
step for this exact purpose, down the call chain leading to
parse_remote_branch() function to change the sample command shown to
the user in this advise message.
Also add a bit more test coverage for this "fail to DWIM under
ambiguity" that we lack, as well as the message we produce when we
fail.
Reported-by: Simon Cheng <cyqsimon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/checkout.c | 29 ++++++++++++++++++++++++-----
t/t2027-checkout-track.sh | 18 ++++++++++++++++++
2 files changed, 42 insertions(+), 5 deletions(-)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4f189fde48..17f31c30b2 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1286,7 +1286,8 @@ enum checkout_command {
static char *parse_remote_branch(const char *arg,
struct object_id *rev,
- int could_be_checkout_paths)
+ int could_be_checkout_paths,
+ enum checkout_command which_command)
{
int num_matches = 0;
char *remote = unique_tracking_name(arg, rev, &num_matches);
@@ -1299,14 +1300,30 @@ static char *parse_remote_branch(const char *arg,
if (!remote && num_matches > 1) {
if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {
+ const char *cmdname;
+
+ switch (which_command) {
+ case CHECKOUT_CHECKOUT:
+ cmdname = "checkout";
+ break;
+ case CHECKOUT_SWITCH:
+ cmdname = "switch";
+ break;
+ default:
+ BUG("command <%d> should not reach parse_remote_branch",
+ which_command);
+ break;
+ }
+
advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
"you can do so by fully qualifying the name with the --track option:\n"
"\n"
- " git checkout --track origin/<name>\n"
+ " git %s --track origin/<name>\n"
"\n"
"If you'd like to always have checkouts of an ambiguous <name> prefer\n"
"one remote, e.g. the 'origin' remote, consider setting\n"
- "checkout.defaultRemote=origin in your config."));
+ "checkout.defaultRemote=origin in your config."),
+ cmdname);
}
die(_("'%s' matched multiple (%d) remote tracking branches"),
@@ -1318,6 +1335,7 @@ static char *parse_remote_branch(const char *arg,
static int parse_branchname_arg(int argc, const char **argv,
int dwim_new_local_branch_ok,
+ enum checkout_command which_command,
struct branch_info *new_branch_info,
struct checkout_opts *opts,
struct object_id *rev)
@@ -1427,7 +1445,8 @@ static int parse_branchname_arg(int argc, const char **argv,
if (recover_with_dwim) {
remote = parse_remote_branch(arg, rev,
- could_be_checkout_paths);
+ could_be_checkout_paths,
+ which_command);
if (remote) {
*new_branch = arg;
arg = remote;
@@ -1916,7 +1935,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
opts->dwim_new_local_branch &&
opts->track == BRANCH_TRACK_UNSPECIFIED &&
!opts->new_branch;
- int n = parse_branchname_arg(argc, argv, dwim_ok,
+ int n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
&new_branch_info, opts, &rev);
argv += n;
argc -= n;
diff --git a/t/t2027-checkout-track.sh b/t/t2027-checkout-track.sh
index a397790df5..c01f1cd617 100755
--- a/t/t2027-checkout-track.sh
+++ b/t/t2027-checkout-track.sh
@@ -47,4 +47,22 @@ test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
test_cmp_config refs/heads/main branch.b4.merge
'
+test_expect_success 'ambiguous tracking info' '
+ # Set up a few remote repositories
+ git init --bare --initial-branch=trunk src1 &&
+ git init --bare --initial-branch=trunk src2 &&
+ git push src1 one:refs/heads/trunk &&
+ git push src2 two:refs/heads/trunk &&
+
+ git remote add -f src1 "file://$PWD/src1" &&
+ git remote add -f src2 "file://$PWD/src2" &&
+
+ # DWIM
+ test_must_fail git checkout trunk 2>hint.checkout &&
+ test_grep "hint: *git checkout --track" hint.checkout &&
+
+ test_must_fail git switch trunk 2>hint.switch &&
+ test_grep "hint: *git switch --track" hint.switch
+'
+
test_done
--
2.53.0-rc2-135-gb1217c0133
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] checkout: pass program-readable token to unified "main"
2026-01-29 19:06 ` [PATCH v2 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
@ 2026-02-06 16:05 ` Patrick Steinhardt
2026-02-19 22:21 ` Junio C Hamano
0 siblings, 1 reply; 11+ messages in thread
From: Patrick Steinhardt @ 2026-02-06 16:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Simon Cheng, Kristoffer Haugsbakk
On Thu, Jan 29, 2026 at 11:06:15AM -0800, Junio C Hamano wrote:
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index f9453473fe..4f189fde48 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
[snip]
> + switch (which_command) {
> + case CHECKOUT_CHECKOUT:
> + usagestr = checkout_usage;
> + break;
> + case CHECKOUT_SWITCH:
> + usagestr = switch_branch_usage;
> + break;
> + case CHECKOUT_RESTORE:
> + usagestr = restore_usage;
> + break;
> + default:
> + BUG("No such checkout variant %d", which_command);
> + }
Tiniest nit, really not worth addressing on its own: BUG messages
typically start with a lower-case letter.
Other than that I like that we have less global constants with this
change.
Patrick
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] checkout: tell "parse_remote_branch" which command is calling it
2026-01-29 19:06 ` [PATCH v2 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
@ 2026-02-06 16:05 ` Patrick Steinhardt
0 siblings, 0 replies; 11+ messages in thread
From: Patrick Steinhardt @ 2026-02-06 16:05 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Simon Cheng, Kristoffer Haugsbakk
On Thu, Jan 29, 2026 at 11:06:16AM -0800, Junio C Hamano wrote:
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 4f189fde48..17f31c30b2 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -1299,14 +1300,30 @@ static char *parse_remote_branch(const char *arg,
>
> if (!remote && num_matches > 1) {
> if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {
> + const char *cmdname;
> +
> + switch (which_command) {
> + case CHECKOUT_CHECKOUT:
> + cmdname = "checkout";
> + break;
> + case CHECKOUT_SWITCH:
> + cmdname = "switch";
> + break;
> + default:
> + BUG("command <%d> should not reach parse_remote_branch",
> + which_command);
> + break;
> + }
`parse_remote_branch()` is only called by `parse_branchname_arg()`,
which in turn is only called if `opts->accept_ref` is truish. And as
that value only gets set to 1 in git-checkout(1) and git-switch(1) we
know that we indeed don't have to care about git-restore(1).
> advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
> "you can do so by fully qualifying the name with the --track option:\n"
> "\n"
> - " git checkout --track origin/<name>\n"
> + " git %s --track origin/<name>\n"
> "\n"
> "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
> "one remote, e.g. the 'origin' remote, consider setting\n"
> - "checkout.defaultRemote=origin in your config."));
> + "checkout.defaultRemote=origin in your config."),
> + cmdname);
> }
>
> die(_("'%s' matched multiple (%d) remote tracking branches"),
Yup, makes sense.
Overall this patch series looks good to me. There's been the one
micronit in the preceding commit, but I really don't think it warrants
a new version.
Thanks!
Patrick
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] checkout: pass program-readable token to unified "main"
2026-02-06 16:05 ` Patrick Steinhardt
@ 2026-02-19 22:21 ` Junio C Hamano
0 siblings, 0 replies; 11+ messages in thread
From: Junio C Hamano @ 2026-02-19 22:21 UTC (permalink / raw)
To: Patrick Steinhardt; +Cc: git, Simon Cheng, Kristoffer Haugsbakk
Patrick Steinhardt <ps@pks.im> writes:
> On Thu, Jan 29, 2026 at 11:06:15AM -0800, Junio C Hamano wrote:
>> diff --git a/builtin/checkout.c b/builtin/checkout.c
>> index f9453473fe..4f189fde48 100644
>> --- a/builtin/checkout.c
>> +++ b/builtin/checkout.c
> [snip]
>> + switch (which_command) {
>> + case CHECKOUT_CHECKOUT:
>> + usagestr = checkout_usage;
>> + break;
>> + case CHECKOUT_SWITCH:
>> + usagestr = switch_branch_usage;
>> + break;
>> + case CHECKOUT_RESTORE:
>> + usagestr = restore_usage;
>> + break;
>> + default:
>> + BUG("No such checkout variant %d", which_command);
>> + }
>
> Tiniest nit, really not worth addressing on its own: BUG messages
> typically start with a lower-case letter.
>
> Other than that I like that we have less global constants with this
> change.
Thanks.
As I locally amended the nit away while queuing, I guess I should
merge this down to 'next'.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-02-19 22:21 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-27 19:29 [PATCH 0/2] Improving advise messages from "switch" Junio C Hamano
2026-01-27 19:29 ` [PATCH 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
2026-01-27 19:29 ` [PATCH 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
2026-01-27 20:35 ` Kristoffer Haugsbakk
2026-01-27 21:22 ` Junio C Hamano
2026-01-29 19:06 ` [PATCH v2 0/2] Improving advise messages from "switch" Junio C Hamano
2026-01-29 19:06 ` [PATCH v2 1/2] checkout: pass program-readable token to unified "main" Junio C Hamano
2026-02-06 16:05 ` Patrick Steinhardt
2026-02-19 22:21 ` Junio C Hamano
2026-01-29 19:06 ` [PATCH v2 2/2] checkout: tell "parse_remote_branch" which command is calling it Junio C Hamano
2026-02-06 16:05 ` Patrick Steinhardt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox