]> git.ipfire.org Git - thirdparty/git.git/commitdiff
checkout: tell "parse_remote_branch" which command is calling it
authorJunio C Hamano <gitster@pobox.com>
Thu, 29 Jan 2026 19:06:16 +0000 (11:06 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 9 Feb 2026 16:33:17 +0000 (08:33 -0800)
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
t/t2027-checkout-track.sh

index 2411108856d60ee1e4b4a9b020eecc2f563805a0..f2c5df9631fed0b66d39ea06a818197aced158ba 100644 (file)
@@ -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;
index a397790df59d0bc015e2b1f244cbbbca27525b7a..c01f1cd617c72b3e8386074100ef812c9db2f753 100755 (executable)
@@ -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