checkout: tell "parse_remote_branch" which command is calling it

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>

Junio C Hamano committed Jan 29, 2026 at 11:06 UTC 12fee11f21e9b63626da5058fa055f95d55a5515
2 files changed +42 -5
builtin/checkout.c
+24 -5
@@ -1286,7 +1286,8 @@ enum checkout_command {
1286
1287 static char *parse_remote_branch(const char *arg,
1288 struct object_id *rev,
1289 - int could_be_checkout_paths)
1289 + int could_be_checkout_paths,
1290 + enum checkout_command which_command)
1291 {
1292 int num_matches = 0;
1293 char *remote = unique_tracking_name(arg, rev, &num_matches);
@@ -1299,14 +1300,30 @@ static char *parse_remote_branch(const char *arg,
1300
1301 if (!remote && num_matches > 1) {
1302 if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {
1303 + const char *cmdname;
1304 +
1305 + switch (which_command) {
1306 + case CHECKOUT_CHECKOUT:
1307 + cmdname = "checkout";
1308 + break;
1309 + case CHECKOUT_SWITCH:
1310 + cmdname = "switch";
1311 + break;
1312 + default:
1313 + BUG("command <%d> should not reach parse_remote_branch",
1314 + which_command);
1315 + break;
1316 + }
1317 +
1318 advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
1319 "you can do so by fully qualifying the name with the --track option:\n"
1320 "\n"
1305 - " git checkout --track origin/<name>\n"
1321 + " git %s --track origin/<name>\n"
1322 "\n"
1323 "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
1324 "one remote, e.g. the 'origin' remote, consider setting\n"
1309 - "checkout.defaultRemote=origin in your config."));
1325 + "checkout.defaultRemote=origin in your config."),
1326 + cmdname);
1327 }
1328
1329 die(_("'%s' matched multiple (%d) remote tracking branches"),
@@ -1318,6 +1335,7 @@ static char *parse_remote_branch(const char *arg,
1335
1336 static int parse_branchname_arg(int argc, const char **argv,
1337 int dwim_new_local_branch_ok,
1338 + enum checkout_command which_command,
1339 struct branch_info *new_branch_info,
1340 struct checkout_opts *opts,
1341 struct object_id *rev)
@@ -1427,7 +1445,8 @@ static int parse_branchname_arg(int argc, const char **argv,
1445
1446 if (recover_with_dwim) {
1447 remote = parse_remote_branch(arg, rev,
1430 - could_be_checkout_paths);
1448 + could_be_checkout_paths,
1449 + which_command);
1450 if (remote) {
1451 *new_branch = arg;
1452 arg = remote;
@@ -1916,7 +1935,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
1935 opts->dwim_new_local_branch &&
1936 opts->track == BRANCH_TRACK_UNSPECIFIED &&
1937 !opts->new_branch;
1919 - int n = parse_branchname_arg(argc, argv, dwim_ok,
1938 + int n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
1939 &new_branch_info, opts, &rev);
1940 argv += n;
1941 argc -= n;
t/t2027-checkout-track.sh
+18
@@ -47,4 +47,22 @@ test_expect_success 'checkout --track -b overrides autoSetupMerge=inherit' '
47 test_cmp_config refs/heads/main branch.b4.merge
48 '
49
50 +test_expect_success 'ambiguous tracking info' '
51 + # Set up a few remote repositories
52 + git init --bare --initial-branch=trunk src1 &&
53 + git init --bare --initial-branch=trunk src2 &&
54 + git push src1 one:refs/heads/trunk &&
55 + git push src2 two:refs/heads/trunk &&
56 +
57 + git remote add -f src1 "file://$PWD/src1" &&
58 + git remote add -f src2 "file://$PWD/src2" &&
59 +
60 + # DWIM
61 + test_must_fail git checkout trunk 2>hint.checkout &&
62 + test_grep "hint: *git checkout --track" hint.checkout &&
63 +
64 + test_must_fail git switch trunk 2>hint.switch &&
65 + test_grep "hint: *git switch --track" hint.switch
66 +'
67 +
68 test_done