{"thread":{"id":"66141","subject":"[PATCH] worktree add: improve message for ambiguous remote branch name","startedAt":"2026-08-08T08:21:43Z","lastAt":"2026-08-27T17:32:46Z","messageCount":62,"participants":["Yoichi NAKAYAMA via GitGitGadget","Junio C Hamano","Harald Nordgren","Yoichi Nakayama","D. Ben Knoble","Ben Knoble"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"550090","messageId":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":null,"subject":"[PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-08T08:21:41Z","receivedAt":"2026-08-08T08:21:43Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nDisplay a descriptive message when DWIM fails.\n\nAdd advice on how to work around this by specifying the fully\nqualified name or by setting checkout.defaultRemote.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n    worktree add: improve message for ambiguous remote branch name\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\n builtin/worktree.c      | 30 ++++++++++++++++++++++++++----\n t/t2400-worktree-add.sh | 21 +++++++++++++++++++--\n 2 files changed, 45 insertions(+), 6 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..46bc305116 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -116,6 +116,16 @@ static const char * const git_worktree_unlock_usage[] = {\n \tNULL\n };\n \n+static const char message_advice_ambiguous_remote_tracking_branch[] =\n+\tN_(\"If you meant to create a worktree from a remote tracking branch on,\\n\"\n+\t   \"e.g. 'origin', you can do so by fully qualifying the name:\\n\"\n+\t   \"\\n\"\n+\t   \"    git worktree add <path> origin/<name>\\n\"\n+\t   \"\\n\"\n+\t   \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t   \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t   \"checkout.defaultRemote=origin in your config.\");\n+\n struct add_opts {\n \tint force;\n \tint detach;\n@@ -764,7 +774,7 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)\n \treturn 1;\n }\n \n-static char *dwim_branch(const char *path, char **new_branch)\n+static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n {\n \tint n;\n \tint branch_exists;\n@@ -781,8 +791,14 @@ static char *dwim_branch(const char *path, char **new_branch)\n \n \t*new_branch = branchname;\n \tif (guess_remote) {\n+\t\tint num_matches = 0;\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n+\t\tif (!opts->quiet && !remote && num_matches > 1) {\n+\t\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n+\t\t\twarning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n+\t\t}\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -890,7 +906,7 @@ static int add(int ac, const char **av, const char *prefix,\n \t\topts.orphan = dwim_orphan(&opts, !!opt_track, 0);\n \t} else if (ac < 2) {\n \t\t/* DWIM: Guess branch name from path. */\n-\t\tchar *s = dwim_branch(path, &new_branch_to_free);\n+\t\tchar *s = dwim_branch(&opts, path, &new_branch_to_free);\n \t\tif (s)\n \t\t\tbranch = branch_to_free = s;\n \t\tnew_branch = new_branch_to_free;\n@@ -904,10 +920,16 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tint num_matches = 0;\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet && advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n+\t\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n+\t\t\t\t}\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"), branch, num_matches);\n \t\t\t}\n \t\t}\n \ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..4d21c8eba4 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n@@ -669,6 +669,23 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '\n \t\ttest_cmp_rev refs/remotes/repo_a/foo refs/heads/foo\n \t)\n '\n+test_expect_success 'git worktree add --guess-remote with ambiguous name' '\n+\ttest_when_finished rm -rf repo_a repo_b foo &&\n+\tsetup_remote_repo repo_a repo_b &&\n+\t(\n+\t\tcd repo_b &&\n+\t\tgit remote add upstream2 ../repo_a &&\n+\t\tgit fetch upstream2 &&\n+\t\tgit worktree add --guess-remote ../foo 2>actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" actual\n+\t) &&\n+\t(\n+\t\tcd foo &&\n+\t\ttest_must_fail git config \"branch.foo.remote\" &&\n+\t\ttest_must_fail git config \"branch.foo.merge\" &&\n+\t\ttest_cmp_rev refs/heads/main refs/heads/foo\n+\t)\n+'\n test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n\nbase-commit: 010afd3166ddc64c9863b1506f12cbcdda0d4ea1\n-- \ngitgitgadget\n"},{"id":"550101","messageId":"xmqqzeywa6ol.fsf@gitster.g","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-08T17:00:26Z","receivedAt":"2026-08-08T17:00:29Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n>\n> Display a descriptive message when DWIM fails.\n>\n> Add advice on how to work around this by specifying the fully\n> qualified name or by setting checkout.defaultRemote.\n>\n> Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n> ---\n>     worktree add: improve message for ambiguous remote branch name\n>\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v1\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v1\n> Pull-Request: https://github.com/gitgitgadget/git/pull/2197\n>\n>  builtin/worktree.c      | 30 ++++++++++++++++++++++++++----\n>  t/t2400-worktree-add.sh | 21 +++++++++++++++++++--\n>  2 files changed, 45 insertions(+), 6 deletions(-)\n>\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> index 654d27c3e1..46bc305116 100644\n> --- a/builtin/worktree.c\n> +++ b/builtin/worktree.c\n> @@ -116,6 +116,16 @@ static const char * const git_worktree_unlock_usage[] = {\n>  \tNULL\n>  };\n>  \n> +static const char message_advice_ambiguous_remote_tracking_branch[] =\n> +\tN_(\"If you meant to create a worktree from a remote tracking branch on,\\n\"\n> +\t   \"e.g. 'origin', you can do so by fully qualifying the name:\\n\"\n> +\t   \"\\n\"\n> +\t   \"    git worktree add <path> origin/<name>\\n\"\n> +\t   \"\\n\"\n\nThis is shown in two places, but what did the user exactly type in\nthese two situations?  Can their intent be different, in which case\ndifferent suggestions might be more appropriate to each of them?\n\nLet's see.\n\n> @@ -781,8 +791,14 @@ static char *dwim_branch(const char *path, char **new_branch)\n>  \n>  \t*new_branch = branchname;\n>  \tif (guess_remote) {\n> +\t\tint num_matches = 0;\n>  \t\tstruct object_id oid;\n> -\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n> +\t\tchar *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n> +\t\tif (!opts->quiet && !remote && num_matches > 1) {\n> +\t\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> +\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n> +\t\t\twarning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n> +\t\t}\n>  \t\treturn remote;\n>  \t}\n\nThe worktree.guessremote configuration is set.  dwim_branch() is\ncalled when \"git worktree add A/B/X\" is run with a single argument\n\"A/B/X\", which comes here as \"path\", and that is munged into the\nbranchname \"X\".\n\nWe used to pass NULL as the second parameter to unique_tracking_name(),\nso we were only interested in the case where we have exactly one\nmatching remote, and if there is 0 or multiple remotes with the\nnamed branch, we returned NULL from here.\n\nThe patch does not change that, but using the branch name, we try to\nsee if there are multiple matches, in that case, we give the advice\nmessage to say \"hey, don't be so lazy, as X appears in more than one\nremote, so tell me which one you mean\".\n\n> @@ -890,7 +906,7 @@ static int add(int ac, const char **av, const char *prefix,\n>  \t\topts.orphan = dwim_orphan(&opts, !!opt_track, 0);\n>  \t} else if (ac < 2) {\n>  \t\t/* DWIM: Guess branch name from path. */\n> -\t\tchar *s = dwim_branch(path, &new_branch_to_free);\n> +\t\tchar *s = dwim_branch(&opts, path, &new_branch_to_free);\n>  \t\tif (s)\n>  \t\t\tbranch = branch_to_free = s;\n>  \t\tnew_branch = new_branch_to_free;\n\nBut shouldn't we do a bit better than \n\n    git worktree add <path> origin/<name>\n\nThe above makes the user think that just like 'git', 'worktree' and\n'add', 'origin/' is a fixed part, and they would need to substitute\n<path> and <name>, but that is not really what we want to tell them.\nThe most crucial part to correct is 'origin/', as that is what we\ncould not guess from the given information.\n\nWe know that the user gave us \"A/B/X\" (path) and probably they want\nto create local \"X\" from it.  Or not.  We also should know, in\ncaller's opt_track and used_new_branch_options, that the user gave\nus \"-t -b Y\" from the command line.\n\n> @@ -904,10 +920,16 @@ static int add(int ac, const char **av, const char *prefix,\n>  \n>  \t\tcommit = lookup_commit_reference_by_name(branch);\n>  \t\tif (!commit) {\n> -\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n> +\t\t\tint num_matches = 0;\n> +\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches);\n>  \t\t\tif (remote) {\n>  \t\t\t\tnew_branch = branch;\n>  \t\t\t\tbranch = new_branch_to_free = remote;\n> +\t\t\t} else if (num_matches > 1) {\n> +\t\t\t\tif (!opts.quiet && advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n> +\t\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n> +\t\t\t\t}\n> +\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"), branch, num_matches);\n\nStyle: overly long line, with {braces} around a single statement block.\n\nWhat does this case handle?  Can you make a similar analysis to come\nup with the list of things we know the user gave us, to give a bit\nbetter command line to suggest here?\n\n>  \t\t\t}\n>  \t\t}\n\nThanks.\n"},{"id":"550112","messageId":"xmqqo6fc9swz.fsf@gitster.g","threadId":"66141","inReplyTo":"xmqqzeywa6ol.fsf@gitster.g","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-08T21:57:48Z","receivedAt":"2026-08-08T21:57:51Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> +static const char message_advice_ambiguous_remote_tracking_branch[] =\n>> +\tN_(\"If you meant to create a worktree from a remote tracking branch on,\\n\"\n>> +\t   \"e.g. 'origin', you can do so by fully qualifying the name:\\n\"\n>> +\t   \"\\n\"\n>> +\t   \"    git worktree add <path> origin/<name>\\n\"\n>> +\t   \"\\n\"\n>> ...\n>> +\t\tchar *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n>> +\t\tif (!opts->quiet && !remote && num_matches > 1) {\n>> +\t\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n>> +\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n>> +\t\t\twarning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n>> +\t\t}\n>>  \t\treturn remote;\n>>  \t}\n>\n> The worktree.guessremote configuration is set.  dwim_branch() is\n> called when \"git worktree add A/B/X\" is run with a single argument\n> \"A/B/X\", which comes here as \"path\", and that is munged into the\n> branchname \"X\".\n>\n> We used to pass NULL as the second parameter to unique_tracking_name(),\n> so we were only interested in the case where we have exactly one\n> matching remote, and if there is 0 or multiple remotes with the\n> named branch, we returned NULL from here.\n>\n> The patch does not change that, but using the branch name, we try to\n> see if there are multiple matches, in that case, we give the advice\n> message to say \"hey, don't be so lazy, as X appears in more than one\n> remote, so tell me which one you mean\".\n\nStepping back a bit, I think what I find lacking in the proposed\nwarning message is not that we lose what the user gave us, such as\n'-b <branch>' or '-t'.  While this loss makes it impossible to\nsimply copy and paste to reproduce what the user may have intended,\nit is not the end of the world.\n\nWhat disturbs me more is that the code holds back information only\nit possesses, which would immediately help the user if we shared it.\n\nThe reason we got this error may not be that the user did not know\nexactly how to spell out the necessary information (such as which\nbranch to use from which remote) on the command line.  It may be\nthat the user did not remember some of the necessary details (such\nas which remotes have the branch they have in mind).  Displaying\nthe command line and advising them to use the fully qualified name\nmight not be the best approach in that case.  Telling them that\nthey may have meant 'origin', 'upstream', or 'home' (all of which\nare remotes with the named branch, though we could not guess which\none of the three to choose) may be much more helpful.\n"},{"id":"550115","messageId":"20260809074528.5189-1-haraldnordgren@gmail.com","threadId":"66141","inReplyTo":"xmqqo6fc9swz.fsf@gitster.g","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Harald Nordgren","fromEmail":"haraldnordgren@gmail.com","sentAt":"2026-08-09T07:45:28Z","receivedAt":"2026-08-09T07:45:37Z","isPatch":true,"body":"This is an interesting idea!\n\n\nHarald\n"},{"id":"550121","messageId":"xmqqjypz9n0s.fsf@gitster.g","threadId":"66141","inReplyTo":"xmqqo6fc9swz.fsf@gitster.g","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-09T18:17:23Z","receivedAt":"2026-08-09T18:17:25Z","isPatch":true,"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n>>> +static const char message_advice_ambiguous_remote_tracking_branch[] =\n>>> +\tN_(\"If you meant to create a worktree from a remote tracking branch on,\\n\"\n>>> +\t   \"e.g. 'origin', you can do so by fully qualifying the name:\\n\"\n>>> +\t   \"\\n\"\n>>> +\t   \"    git worktree add <path> origin/<name>\\n\"\n>>> +\t   \"\\n\"\n>>> ...\n>>> +\t\tchar *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n>>> +\t\tif (!opts->quiet && !remote && num_matches > 1) {\n>>> +\t\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n>>> +\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n>>> +\t\t\twarning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n>>> +\t\t}\n\nSorry for piecemeal reviews, but I just noticed that you have a\nterminating LF at the end of a single-liner warning message.  As\ndie/error/warning ffamily of helpers give the terminating newline\nthemselves, you must not.  Unless you want to leave a blank line\nafter your message, that is.\n\nThanks.\n\n"},{"id":"550122","messageId":"xmqqfr0n9mxp.fsf@gitster.g","threadId":"66141","inReplyTo":"20260809074528.5189-1-haraldnordgren@gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-09T18:19:14Z","receivedAt":"2026-08-09T18:19:16Z","isPatch":true,"body":"Harald Nordgren <haraldnordgren@gmail.com> writes:\n\n> This is an interesting idea!\n>\n>\n> Harald\n\nWhen expressing your opinion on what another said, quote a bit from\nthe message you are responding to so that people know what you are\nreferring to.  I cannot easily tell which part of what I said you\nfound interesting.\n\nThanks.\n"},{"id":"550165","messageId":"CAHwyqnXGOCZM6iMg0B_2Lxbyp9ux5Vsj67C_u71o0ZivBQ-w0w@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqfr0n9mxp.fsf@gitster.g","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Harald Nordgren","fromEmail":"haraldnordgren@gmail.com","sentAt":"2026-08-10T10:12:55Z","receivedAt":"2026-08-10T10:13:34Z","isPatch":true,"body":"> When expressing your opinion on what another said, quote a bit from\n> the message you are responding to so that people know what you are\n> referring to.  I cannot easily tell which part of what I said you\n> found interesting.\n\nSorry, yes this was about:\n\n> Telling them that\n> they may have meant 'origin', 'upstream', or 'home' (all of which\n> are remotes with the named branch, though we could not guess which\n> one of the three to choose) may be much more helpful.\n\nHarald\n"},{"id":"550178","messageId":"CAF5D8-tvqwN6+ZcrRyy2Dbiv5KttrsT_rr07XPUFbtD7TLZrVg@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqzeywa6ol.fsf@gitster.g","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-10T13:00:25Z","receivedAt":"2026-08-10T13:00:38Z","isPatch":true,"body":"On Sun, Aug 9, 2026 at 2:00 AM Junio C Hamano <gitster@pobox.com> wrote:\n> > @@ -781,8 +791,14 @@ static char *dwim_branch(const char *path, char **new_branch)\n> >\n> >       *new_branch = branchname;\n> >       if (guess_remote) {\n> > +             int num_matches = 0;\n> >               struct object_id oid;\n> > -             char *remote = unique_tracking_name(*new_branch, &oid, NULL);\n> > +             char *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n> > +             if (!opts->quiet && !remote && num_matches > 1) {\n> > +                     if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> > +                             advise(_(message_advice_ambiguous_remote_tracking_branch));\n> > +                     warning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n> > +             }\n> >               return remote;\n> >       }\n>\n> The worktree.guessremote configuration is set.  dwim_branch() is\n> called when \"git worktree add A/B/X\" is run with a single argument\n> \"A/B/X\", which comes here as \"path\", and that is munged into the\n> branchname \"X\".\n>\n> We used to pass NULL as the second parameter to unique_tracking_name(),\n> so we were only interested in the case where we have exactly one\n> matching remote, and if there is 0 or multiple remotes with the\n> named branch, we returned NULL from here.\n>\n> The patch does not change that, but using the branch name, we try to\n> see if there are multiple matches, in that case, we give the advice\n> message to say \"hey, don't be so lazy, as X appears in more than one\n> remote, so tell me which one you mean\".\n\nI thought the problem here was that it was impossible to distinguish whether\nthe guess was successful, but it was not true. We can distinguish by\nthe message:\n    branch 'name' set up to track 'remote/name'.\nI will not make changes to this part.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550179","messageId":"CAF5D8-vEwG=3rr99F4TW2WMJksts5BOZ+hoN0=kNkT6dUwqr1w@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqo6fc9swz.fsf@gitster.g","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-10T13:04:50Z","receivedAt":"2026-08-10T13:05:04Z","isPatch":true,"body":"On Sun, Aug 9, 2026 at 6:57 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Junio C Hamano <gitster@pobox.com> writes:\n>\n> >> +static const char message_advice_ambiguous_remote_tracking_branch[] =\n> >> +    N_(\"If you meant to create a worktree from a remote tracking branch on,\\n\"\n> >> +       \"e.g. 'origin', you can do so by fully qualifying the name:\\n\"\n> >> +       \"\\n\"\n> >> +       \"    git worktree add <path> origin/<name>\\n\"\n> >> +       \"\\n\"\n> >> ...\n> >> +            char *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n> >> +            if (!opts->quiet && !remote && num_matches > 1) {\n> >> +                    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> >> +                            advise(_(message_advice_ambiguous_remote_tracking_branch));\n> >> +                    warning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n> >> +            }\n> >>              return remote;\n> >>      }\n> >\n> > The worktree.guessremote configuration is set.  dwim_branch() is\n> > called when \"git worktree add A/B/X\" is run with a single argument\n> > \"A/B/X\", which comes here as \"path\", and that is munged into the\n> > branchname \"X\".\n> >\n> > We used to pass NULL as the second parameter to unique_tracking_name(),\n> > so we were only interested in the case where we have exactly one\n> > matching remote, and if there is 0 or multiple remotes with the\n> > named branch, we returned NULL from here.\n> >\n> > The patch does not change that, but using the branch name, we try to\n> > see if there are multiple matches, in that case, we give the advice\n> > message to say \"hey, don't be so lazy, as X appears in more than one\n> > remote, so tell me which one you mean\".\n>\n> Stepping back a bit, I think what I find lacking in the proposed\n> warning message is not that we lose what the user gave us, such as\n> '-b <branch>' or '-t'.  While this loss makes it impossible to\n> simply copy and paste to reproduce what the user may have intended,\n> it is not the end of the world.\n>\n> What disturbs me more is that the code holds back information only\n> it possesses, which would immediately help the user if we shared it.\n>\n> The reason we got this error may not be that the user did not know\n> exactly how to spell out the necessary information (such as which\n> branch to use from which remote) on the command line.  It may be\n> that the user did not remember some of the necessary details (such\n> as which remotes have the branch they have in mind).  Displaying\n> the command line and advising them to use the fully qualified name\n> might not be the best approach in that case.  Telling them that\n> they may have meant 'origin', 'upstream', or 'home' (all of which\n> are remotes with the named branch, though we could not guess which\n> one of the three to choose) may be much more helpful.\n\nI realized that instead of placing a burden on the user, we should\npresent a solution.\n\nWhen a multiple match occurs, the only decision the user needs to make\nis which remote to select.\nFor everything else, the hint should give a specific command with\narguments that achieve the same\nbehavior as when exactly one remote matches.\n\nRather than presenting a list of candidates, I think it is preferable\nto explain how to generate that list.\nThis allows users to process the list e.g. by piping it into a command.\n\nI'll submit an updated patch.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550180","messageId":"CALnO6CAdr0ft8KFgGCFX9ueKUdX9-2DwB+SNs3Q8ykw4ne=54Q@mail.gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-08-10T13:07:57Z","receivedAt":"2026-08-10T13:08:10Z","isPatch":true,"body":"Hi Yoichi,\n\nOn Sat, Aug 8, 2026 at 4:21 AM Yoichi NAKAYAMA via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n>\n> Display a descriptive message when DWIM fails.\n>\n> Add advice on how to work around this by specifying the fully\n> qualified name or by setting checkout.defaultRemote.\n>\n> Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n> ---\n\n[snip]\n\n> -static char *dwim_branch(const char *path, char **new_branch)\n> +static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n>  {\n>         int n;\n>         int branch_exists;\n> @@ -781,8 +791,14 @@ static char *dwim_branch(const char *path, char **new_branch)\n>\n>         *new_branch = branchname;\n>         if (guess_remote) {\n> +               int num_matches = 0;\n>                 struct object_id oid;\n> -               char *remote = unique_tracking_name(*new_branch, &oid, NULL);\n> +               char *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n> +               if (!opts->quiet && !remote && num_matches > 1) {\n> +                       if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> +                               advise(_(message_advice_ambiguous_remote_tracking_branch));\n> +                       warning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n> +               }\n>                 return remote;\n>         }\n>         return NULL;\n\nI suppose the extra warning won't hurt anyone's workflow :) so that's good.\n\n[snip]\n\n> @@ -904,10 +920,16 @@ static int add(int ac, const char **av, const char *prefix,\n>\n>                 commit = lookup_commit_reference_by_name(branch);\n>                 if (!commit) {\n> -                       remote = unique_tracking_name(branch, &oid, NULL);\n> +                       int num_matches = 0;\n> +                       remote = unique_tracking_name(branch, &oid, &num_matches);\n>                         if (remote) {\n>                                 new_branch = branch;\n>                                 branch = new_branch_to_free = remote;\n> +                       } else if (num_matches > 1) {\n> +                               if (!opts.quiet && advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n> +                                       advise(_(message_advice_ambiguous_remote_tracking_branch));\n> +                               }\n> +                               die(_(\"'%s' matched multiple (%d) remote tracking branches\"), branch, num_matches);\n>                         }\n>                 }\n\nWe would now die() here where we didn't before. I'm not suggesting\nthat is wrong (I haven't given it much thought), but I was surprised\nto see it in the code without mention in the message, which I've left\nquoted above. In particular, the proposed log message talks about\ngiving new advice, so I wasn't expecting us to abort.\n\nNow, it may be that this case already causes an error later on (I\nhaven't analyzed that), in which case dying early with a better\ndiagnostic is definitely helpful. If that's the case, it would be nice\nto spell that out for the rest of us :)\n\nIf not, I would want to know why we can die() here without bothering\nanyone's workflow that is expecting us to carry on.\n\nThanks!\n\n-- \nD. Ben Knoble\n"},{"id":"550186","messageId":"CAF5D8-vkd3GOOpdaC=swLv-gxtCzkdhNNUpbTSwr2PBH_Jk=1w@mail.gmail.com","threadId":"66141","inReplyTo":"CALnO6CAdr0ft8KFgGCFX9ueKUdX9-2DwB+SNs3Q8ykw4ne=54Q@mail.gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-10T13:35:44Z","receivedAt":"2026-08-10T13:35:57Z","isPatch":true,"body":"On Mon, Aug 10, 2026 at 10:08 PM D. Ben Knoble <ben.knoble@gmail.com> wrote:\n> > @@ -904,10 +920,16 @@ static int add(int ac, const char **av, const char *prefix,\n> >\n> >                 commit = lookup_commit_reference_by_name(branch);\n> >                 if (!commit) {\n> > -                       remote = unique_tracking_name(branch, &oid, NULL);\n> > +                       int num_matches = 0;\n> > +                       remote = unique_tracking_name(branch, &oid, &num_matches);\n> >                         if (remote) {\n> >                                 new_branch = branch;\n> >                                 branch = new_branch_to_free = remote;\n> > +                       } else if (num_matches > 1) {\n> > +                               if (!opts.quiet && advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n> > +                                       advise(_(message_advice_ambiguous_remote_tracking_branch));\n> > +                               }\n> > +                               die(_(\"'%s' matched multiple (%d) remote tracking branches\"), branch, num_matches);\n> >                         }\n> >                 }\n>\n> We would now die() here where we didn't before. I'm not suggesting\n> that is wrong (I haven't given it much thought), but I was surprised\n> to see it in the code without mention in the message, which I've left\n> quoted above. In particular, the proposed log message talks about\n> giving new advice, so I wasn't expecting us to abort.\n>\n> Now, it may be that this case already causes an error later on (I\n> haven't analyzed that), in which case dying early with a better\n> diagnostic is definitely helpful. If that's the case, it would be nice\n> to spell that out for the rest of us :)\n>\n> If not, I would want to know why we can die() here without bothering\n> anyone's workflow that is expecting us to carry on.\n\nBefore the change, it calles lookup_commit_reference_by_name() again\nin the if condition and die() at:\n\n    if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {\n        /* snip */\n        die(_(\"invalid reference: %s\"), branch);\n    }\n\nThe motivation for the fix was that this error message did not\naccurately reflect the situation.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550190","messageId":"xmqqse4m6mlz.fsf@gitster.g","threadId":"66141","inReplyTo":"CAF5D8-vkd3GOOpdaC=swLv-gxtCzkdhNNUpbTSwr2PBH_Jk=1w@mail.gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-10T15:06:48Z","receivedAt":"2026-08-10T15:06:51Z","isPatch":true,"body":"Yoichi Nakayama <yoichi.nakayama@gmail.com> writes:\n\n> Before the change, it calles lookup_commit_reference_by_name() again\n> in the if condition and die() at:\n>\n>     if (!opts.orphan && !lookup_commit_reference_by_name(branch)) {\n>         /* snip */\n>         die(_(\"invalid reference: %s\"), branch);\n>     }\n>\n> The motivation for the fix was that this error message did not\n> accurately reflect the situation.\n\nThe location of this die() is a tad away from the places that the\npatch touched.  The proposed log message could be made a bit more\nhelpful by mentioning it.  What was posted reads:\n\n    Display a descriptive message when DWIM fails.\n\n    Add advice on how to work around this by specifying the fully\n    qualified name or by setting checkout.defaultRemote.\n\nbut telling the readers what they will see instead of a descriptive\nmessage and how that happens would be very helpful to understand why\nit is a good idea to die early.  Perhaps\n\n    When the user runs 'git worktree add x y z' command that does\n    not exactly say which remote they want to work with, we try to\n    guess which remote by passing y.  If there are multiple remotes\n    that have branch named y, we silently gave up, leaving remote\n    still NULL.  This later causes A and B not happen, and we end up\n    with passing an non-existing branch to\n    lookup_commit_reference_by_name(), triggering \"invalid\n    reference\" error and die.\n\nor something like that that describes the issue to a similar degree\nas above mock-up message.\n\nThanks.\n"},{"id":"550191","messageId":"pull.2197.v2.git.1786374470383.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v2] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-10T15:07:49Z","receivedAt":"2026-08-10T15:07:54Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nDisplay a hint and a descriptive error message when DWIM fails.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n    worktree add: improve message for ambiguous remote branch name\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v1:\n\n 1:  00b814fe09 ! 1:  1bc57ce497 worktree add: improve message for ambiguous remote branch name\n     @@ Metadata\n       ## Commit message ##\n          worktree add: improve message for ambiguous remote branch name\n      \n     -    Display a descriptive message when DWIM fails.\n     -\n     -    Add advice on how to work around this by specifying the fully\n     -    qualified name or by setting checkout.defaultRemote.\n     +    Display a hint and a descriptive error message when DWIM fails.\n      \n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n       ## builtin/worktree.c ##\n     -@@ builtin/worktree.c: static const char * const git_worktree_unlock_usage[] = {\n     - \tNULL\n     - };\n     +@@\n     + \t\"\\n\" \\\n     + \t\"    git worktree add --orphan %s\\n\")\n       \n     -+static const char message_advice_ambiguous_remote_tracking_branch[] =\n     -+\tN_(\"If you meant to create a worktree from a remote tracking branch on,\\n\"\n     -+\t   \"e.g. 'origin', you can do so by fully qualifying the name:\\n\"\n     -+\t   \"\\n\"\n     -+\t   \"    git worktree add <path> origin/<name>\\n\"\n     -+\t   \"\\n\"\n     -+\t   \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n     -+\t   \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n     -+\t   \"checkout.defaultRemote=origin in your config.\");\n     ++#define WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT \\\n     ++\t_(\"Matched multiple remote tracking branches, you can list them by:\\n\" \\\n     ++\t\"\\n\" \\\n     ++\t\"    git branch -r --list \\\"*/%s\\\"\\n\" \\\n     ++\t\"\\n\" \\\n     ++\t\"If you meant to create a worktree from a remote tracking branch on,\\n\" \\\n     ++\t\"e.g. 'origin', you can do so by:\\n\" \\\n     ++\t\"\\n\" \\\n     ++\t\"    git worktree add -b %s %s origin/%s\\n\" \\\n     ++\t\"\\n\" \\\n     ++\t\"If you'd like to always prefer some remote, e.g. 'origin',\\n\" \\\n     ++\t\"consider setting checkout.defaultRemote=origin in your config.\")\n      +\n     - struct add_opts {\n     - \tint force;\n     - \tint detach;\n     -@@ builtin/worktree.c: static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)\n     - \treturn 1;\n     - }\n     - \n     --static char *dwim_branch(const char *path, char **new_branch)\n     -+static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n     - {\n     - \tint n;\n     - \tint branch_exists;\n     -@@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch)\n     - \n     - \t*new_branch = branchname;\n     - \tif (guess_remote) {\n     -+\t\tint num_matches = 0;\n     - \t\tstruct object_id oid;\n     --\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n     -+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n     -+\t\tif (!opts->quiet && !remote && num_matches > 1) {\n     -+\t\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n     -+\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n     -+\t\t\twarning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n     -+\t\t}\n     - \t\treturn remote;\n     - \t}\n     - \treturn NULL;\n     -@@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n     - \t\topts.orphan = dwim_orphan(&opts, !!opt_track, 0);\n     - \t} else if (ac < 2) {\n     - \t\t/* DWIM: Guess branch name from path. */\n     --\t\tchar *s = dwim_branch(path, &new_branch_to_free);\n     -+\t\tchar *s = dwim_branch(&opts, path, &new_branch_to_free);\n     - \t\tif (s)\n     - \t\t\tbranch = branch_to_free = s;\n     - \t\tnew_branch = new_branch_to_free;\n     + static const char * const git_worktree_usage[] = {\n     + \tBUILTIN_WORKTREE_ADD_USAGE,\n     + \tBUILTIN_WORKTREE_LIST_USAGE,\n      @@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n       \n       \t\tcommit = lookup_commit_reference_by_name(branch);\n     @@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n       \t\t\t\tnew_branch = branch;\n       \t\t\t\tbranch = new_branch_to_free = remote;\n      +\t\t\t} else if (num_matches > 1) {\n     -+\t\t\t\tif (!opts.quiet && advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n     -+\t\t\t\t\tadvise(_(message_advice_ambiguous_remote_tracking_branch));\n     -+\t\t\t\t}\n     -+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"), branch, num_matches);\n     ++\t\t\t\tif (!opts.quiet)\n     ++\t\t\t\t\tadvise_if_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,\n     ++\t\t\t\t\t\t\t  WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT,\n     ++\t\t\t\t\t\t\t  branch, branch, path, branch);\n     ++\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n     ++\t\t\t\t    branch, num_matches);\n       \t\t\t}\n       \t\t}\n       \n     @@ t/t2400-worktree-add.sh: test_expect_success '\"add\" <path> <branch> dwims' '\n       \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n       \t\tgit status -uno --porcelain >status.actual &&\n       \t\ttest_must_be_empty status.actual\n     -@@ t/t2400-worktree-add.sh: test_expect_success 'git worktree add --guess-remote sets up tracking' '\n     - \t\ttest_cmp_rev refs/remotes/repo_a/foo refs/heads/foo\n     - \t)\n     - '\n     -+test_expect_success 'git worktree add --guess-remote with ambiguous name' '\n     -+\ttest_when_finished rm -rf repo_a repo_b foo &&\n     -+\tsetup_remote_repo repo_a repo_b &&\n     -+\t(\n     -+\t\tcd repo_b &&\n     -+\t\tgit remote add upstream2 ../repo_a &&\n     -+\t\tgit fetch upstream2 &&\n     -+\t\tgit worktree add --guess-remote ../foo 2>actual &&\n     -+\t\ttest_grep \"matched multiple (2) remote tracking branches\" actual\n     -+\t) &&\n     -+\t(\n     -+\t\tcd foo &&\n     -+\t\ttest_must_fail git config \"branch.foo.remote\" &&\n     -+\t\ttest_must_fail git config \"branch.foo.merge\" &&\n     -+\t\ttest_cmp_rev refs/heads/main refs/heads/foo\n     -+\t)\n     -+'\n     - test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '\n     - \ttest_when_finished rm -rf repo_a repo_b foo &&\n     - \tsetup_remote_repo repo_a repo_b &&\n\n\n builtin/worktree.c      | 23 ++++++++++++++++++++++-\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 24 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..b29c3a3755 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -64,6 +64,19 @@\n \t\"\\n\" \\\n \t\"    git worktree add --orphan %s\\n\")\n \n+#define WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT \\\n+\t_(\"Matched multiple remote tracking branches, you can list them by:\\n\" \\\n+\t\"\\n\" \\\n+\t\"    git branch -r --list \\\"*/%s\\\"\\n\" \\\n+\t\"\\n\" \\\n+\t\"If you meant to create a worktree from a remote tracking branch on,\\n\" \\\n+\t\"e.g. 'origin', you can do so by:\\n\" \\\n+\t\"\\n\" \\\n+\t\"    git worktree add -b %s %s origin/%s\\n\" \\\n+\t\"\\n\" \\\n+\t\"If you'd like to always prefer some remote, e.g. 'origin',\\n\" \\\n+\t\"consider setting checkout.defaultRemote=origin in your config.\")\n+\n static const char * const git_worktree_usage[] = {\n \tBUILTIN_WORKTREE_ADD_USAGE,\n \tBUILTIN_WORKTREE_LIST_USAGE,\n@@ -904,10 +917,18 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tint num_matches = 0;\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet)\n+\t\t\t\t\tadvise_if_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,\n+\t\t\t\t\t\t\t  WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT,\n+\t\t\t\t\t\t\t  branch, branch, path, branch);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n \t\t}\n \ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n\nbase-commit: 010afd3166ddc64c9863b1506f12cbcdda0d4ea1\n-- \ngitgitgadget\n"},{"id":"550222","messageId":"pull.2197.v3.git.1786395305884.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-10T20:55:05Z","receivedAt":"2026-08-10T20:55:08Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add x y' command that does not\nexactly say which remote they want to work with, and there is no local\nbranch named y, we try to guess which remote by passing y then create\na new branch named y which tracks the remote branch.\n\nIf there are multiple remotes that have branch named y, we silently\ngave up, leaving the variable branch intact.  This later causes\ncreating local branch and worktree not happen, and we end up with\npassing an non-existing branch to lookup_commit_reference_by_name(),\ntriggering \"invalid reference\" error and die.\n\nTo resolve this issue, display a hint and a descriptive error message\nand die immediately when multiple mathing branches are found.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n    worktree add: improve message for ambiguous remote branch name\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v2:\n\n 1:  1bc57ce497 ! 1:  1b9364da7e worktree add: improve message for ambiguous remote branch name\n     @@ Metadata\n       ## Commit message ##\n          worktree add: improve message for ambiguous remote branch name\n      \n     -    Display a hint and a descriptive error message when DWIM fails.\n     +    When the user runs 'git worktree add x y' command that does not\n     +    exactly say which remote they want to work with, and there is no local\n     +    branch named y, we try to guess which remote by passing y then create\n     +    a new branch named y which tracks the remote branch.\n     +\n     +    If there are multiple remotes that have branch named y, we silently\n     +    gave up, leaving the variable branch intact.  This later causes\n     +    creating local branch and worktree not happen, and we end up with\n     +    passing an non-existing branch to lookup_commit_reference_by_name(),\n     +    triggering \"invalid reference\" error and die.\n     +\n     +    To resolve this issue, display a hint and a descriptive error message\n     +    and die immediately when multiple mathing branches are found.\n      \n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n\n\n builtin/worktree.c      | 23 ++++++++++++++++++++++-\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 24 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..b29c3a3755 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -64,6 +64,19 @@\n \t\"\\n\" \\\n \t\"    git worktree add --orphan %s\\n\")\n \n+#define WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT \\\n+\t_(\"Matched multiple remote tracking branches, you can list them by:\\n\" \\\n+\t\"\\n\" \\\n+\t\"    git branch -r --list \\\"*/%s\\\"\\n\" \\\n+\t\"\\n\" \\\n+\t\"If you meant to create a worktree from a remote tracking branch on,\\n\" \\\n+\t\"e.g. 'origin', you can do so by:\\n\" \\\n+\t\"\\n\" \\\n+\t\"    git worktree add -b %s %s origin/%s\\n\" \\\n+\t\"\\n\" \\\n+\t\"If you'd like to always prefer some remote, e.g. 'origin',\\n\" \\\n+\t\"consider setting checkout.defaultRemote=origin in your config.\")\n+\n static const char * const git_worktree_usage[] = {\n \tBUILTIN_WORKTREE_ADD_USAGE,\n \tBUILTIN_WORKTREE_LIST_USAGE,\n@@ -904,10 +917,18 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tint num_matches = 0;\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet)\n+\t\t\t\t\tadvise_if_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,\n+\t\t\t\t\t\t\t  WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT,\n+\t\t\t\t\t\t\t  branch, branch, path, branch);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n \t\t}\n \ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n\nbase-commit: 010afd3166ddc64c9863b1506f12cbcdda0d4ea1\n-- \ngitgitgadget\n"},{"id":"550224","messageId":"CAF5D8-u23Z=f02vF1yAHGKRED8DY-v5=BNf7w-yY3vEDmJChDg@mail.gmail.com","threadId":"66141","inReplyTo":"CALnO6CAdr0ft8KFgGCFX9ueKUdX9-2DwB+SNs3Q8ykw4ne=54Q@mail.gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-10T21:36:19Z","receivedAt":"2026-08-10T21:36:32Z","isPatch":true,"body":"On Mon, Aug 10, 2026 at 10:08 PM D. Ben Knoble <ben.knoble@gmail.com> wrote:\n> > -static char *dwim_branch(const char *path, char **new_branch)\n> > +static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n> >  {\n> >         int n;\n> >         int branch_exists;\n> > @@ -781,8 +791,14 @@ static char *dwim_branch(const char *path, char **new_branch)\n> >\n> >         *new_branch = branchname;\n> >         if (guess_remote) {\n> > +               int num_matches = 0;\n> >                 struct object_id oid;\n> > -               char *remote = unique_tracking_name(*new_branch, &oid, NULL);\n> > +               char *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n> > +               if (!opts->quiet && !remote && num_matches > 1) {\n> > +                       if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> > +                               advise(_(message_advice_ambiguous_remote_tracking_branch));\n> > +                       warning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n> > +               }\n> >                 return remote;\n> >         }\n> >         return NULL;\n>\n> I suppose the extra warning won't hurt anyone's workflow :) so that's good.\n\nI removed the change (advise and warn) here in the latest patch. But I am still\nwondering what I should do. I think a warning would be excessive if\nthere is no match,\nbut the user might want to know if there are multiple matches.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550227","messageId":"xmqqecg55xqq.fsf@gitster.g","threadId":"66141","inReplyTo":"pull.2197.v3.git.1786395305884.gitgitgadget@gmail.com","subject":"Re: [PATCH v3] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-11T00:03:57Z","receivedAt":"2026-08-11T00:04:01Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n>\n> When the user runs 'git worktree add x y' command that does not\n> exactly say which remote they want to work with, and there is no local\n> branch named y, we try to guess which remote by passing y then create\n> a new branch named y which tracks the remote branch.\n\nI used x and y as placeholders.  The readers would be helped if you\nused a more plausible sounding names, e.g., naming directory as\nsomething like foo-dir (the point being 'dir' somewhere in its name)\nand naming a branch as something like bar-topic.  If this were 'git\nworktree add', it is probably more than likely that the destination\ndirectory would begin with ../ to have the new worktree next to the\nprimary repository we are running in, no?\n\n> If there are multiple remotes that have branch named y, we silently\n> gave up, leaving the variable branch intact.  This later causes\n> creating local branch and worktree not happen, and we end up with\n> passing an non-existing branch to lookup_commit_reference_by_name(),\n> triggering \"invalid reference\" error and die.\n\n\"This later causes\" part still seems a bit too sketchy to help a\ntotally new reader, even though I've stared at this code long enough\nso it would be sufficient for me personally.  But these logs are not\nabout helping me, but helping other developers, so...\n\n> +#define WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT \\\n> +\t_(\"Matched multiple remote tracking branches, you can list them by:\\n\" \\\n> +\t\"\\n\" \\\n> +\t\"    git branch -r --list \\\"*/%s\\\"\\n\" \\\n> +\t\"\\n\" \\\n> +\t\"If you meant to create a worktree from a remote tracking branch on,\\n\" \\\n> +\t\"e.g. 'origin', you can do so by:\\n\" \\\n> +\t\"\\n\" \\\n> +\t\"    git worktree add -b %s %s origin/%s\\n\" \\\n> +\t\"\\n\" \\\n> +\t\"If you'd like to always prefer some remote, e.g. 'origin',\\n\" \\\n> +\t\"consider setting checkout.defaultRemote=origin in your config.\")\n\nInstead of throwing the problem back to the user with four extra\nlines of message telling them how to run 'git branch', I would have\nexpected this patch to teach unique_tracking_name() to optionally\nreturn the list of remotes with that branch name, and to use that\nresult in this message.  However, if the goal is simply to provide\nsomething better than 'invalid reference', we do not even need to\ngo that far.  Just stating that branch 'y' appears on multiple\nremotes and asking them to clarify which one they mean might be a\nsufficient improvement.\n\nCould the original request be aiming to create a new worktree with\nthe HEAD detached at the commit pointed at by the remote-tracking\nbranch, instead of creating a local branch forked from it?  I am\njust wondering if \"-b %s\" is too specific to one possible\ninterpretation that may contradict to what the user actually wanted\nto do.\n\nThanks.\n\n"},{"id":"550242","messageId":"CAF5D8-uCjA-MFtdBCa0+5PDb-LFJ7JJ0yK1AtuWCKEN+tKQa_Q@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqecg55xqq.fsf@gitster.g","subject":"Re: [PATCH v3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-11T06:31:54Z","receivedAt":"2026-08-11T06:32:07Z","isPatch":true,"body":"On Tue, Aug 11, 2026 at 9:04 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n> >\n> > When the user runs 'git worktree add x y' command that does not\n> > exactly say which remote they want to work with, and there is no local\n> > branch named y, we try to guess which remote by passing y then create\n> > a new branch named y which tracks the remote branch.\n>\n> I used x and y as placeholders.  The readers would be helped if you\n> used a more plausible sounding names, e.g., naming directory as\n> something like foo-dir (the point being 'dir' somewhere in its name)\n> and naming a branch as something like bar-topic.  If this were 'git\n> worktree add', it is probably more than likely that the destination\n> directory would begin with ../ to have the new worktree next to the\n> primary repository we are running in, no?\n>\n> > If there are multiple remotes that have branch named y, we silently\n> > gave up, leaving the variable branch intact.  This later causes\n> > creating local branch and worktree not happen, and we end up with\n> > passing an non-existing branch to lookup_commit_reference_by_name(),\n> > triggering \"invalid reference\" error and die.\n>\n> \"This later causes\" part still seems a bit too sketchy to help a\n> totally new reader, even though I've stared at this code long enough\n> so it would be sufficient for me personally.  But these logs are not\n> about helping me, but helping other developers, so...\n>\n> > +#define WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT \\\n> > +     _(\"Matched multiple remote tracking branches, you can list them by:\\n\" \\\n> > +     \"\\n\" \\\n> > +     \"    git branch -r --list \\\"*/%s\\\"\\n\" \\\n> > +     \"\\n\" \\\n> > +     \"If you meant to create a worktree from a remote tracking branch on,\\n\" \\\n> > +     \"e.g. 'origin', you can do so by:\\n\" \\\n> > +     \"\\n\" \\\n> > +     \"    git worktree add -b %s %s origin/%s\\n\" \\\n> > +     \"\\n\" \\\n> > +     \"If you'd like to always prefer some remote, e.g. 'origin',\\n\" \\\n> > +     \"consider setting checkout.defaultRemote=origin in your config.\")\n>\n> Instead of throwing the problem back to the user with four extra\n> lines of message telling them how to run 'git branch', I would have\n> expected this patch to teach unique_tracking_name() to optionally\n> return the list of remotes with that branch name, and to use that\n> result in this message.  However, if the goal is simply to provide\n> something better than 'invalid reference', we do not even need to\n> go that far.  Just stating that branch 'y' appears on multiple\n> remotes and asking them to clarify which one they mean might be a\n> sufficient improvement.\n\nExtending `unique_tracking_name()` would also affect the implementation\nin `checkout.c`, and since the goal here is to improve the messages\n(making them as helpful as those in `checkout`), I will hold off on doing\nthat for now.\n\nI will rewrite the log messages a bit.\n\n\n> Could the original request be aiming to create a new worktree with\n> the HEAD detached at the commit pointed at by the remote-tracking\n> branch, instead of creating a local branch forked from it?  I am\n> just wondering if \"-b %s\" is too specific to one possible\n> interpretation that may contradict to what the user actually wanted\n> to do.\n\nIf the user is aiming to create a new worktree with the HEAD detached,\none would specify a fully qualified branch name like origin/bar-topic,\nstarting with a remote name.  If a branch name starts with a remote name,\nmultiple matches (condition to show this hint) rarely occur.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550243","messageId":"pull.2197.v4.git.1786430155244.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v4] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-11T06:35:54Z","receivedAt":"2026-08-11T06:36:02Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add ../foo-dir bar-topic' command\nthat does not exactly say which remote they want to work with, and\nthere is no local branch named bar-topic, we try to guess which remote\nby passing bar-topic then create a new branch named bar-topic which\ntracks the remote branch.\n\nIf there are multiple remotes that have branch named bar-topic, we\nsilently gave up, leaving the variable 'branch' intact.  Then we\nentered the conditional clause 'if (!opts.orphan &&\n!lookup_commit_reference_by_name(branch))' and triggered \"invalid\nreference\" error.  This error message did not contain enough\ninformation to resolve the issue where the remote could not be\nguessed.\n\nTo improve the situation, we display a hint and a descriptive error\nmessage and die immediately when multiple matching branches are found.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n    worktree add: improve message for ambiguous remote branch name\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v3:\n\n 1:  1b9364da7e ! 1:  f7c413b588 worktree add: improve message for ambiguous remote branch name\n     @@ Metadata\n       ## Commit message ##\n          worktree add: improve message for ambiguous remote branch name\n      \n     -    When the user runs 'git worktree add x y' command that does not\n     -    exactly say which remote they want to work with, and there is no local\n     -    branch named y, we try to guess which remote by passing y then create\n     -    a new branch named y which tracks the remote branch.\n     +    When the user runs 'git worktree add ../foo-dir bar-topic' command\n     +    that does not exactly say which remote they want to work with, and\n     +    there is no local branch named bar-topic, we try to guess which remote\n     +    by passing bar-topic then create a new branch named bar-topic which\n     +    tracks the remote branch.\n      \n     -    If there are multiple remotes that have branch named y, we silently\n     -    gave up, leaving the variable branch intact.  This later causes\n     -    creating local branch and worktree not happen, and we end up with\n     -    passing an non-existing branch to lookup_commit_reference_by_name(),\n     -    triggering \"invalid reference\" error and die.\n     +    If there are multiple remotes that have branch named bar-topic, we\n     +    silently gave up, leaving the variable 'branch' intact.  Then we\n     +    entered the conditional clause 'if (!opts.orphan &&\n     +    !lookup_commit_reference_by_name(branch))' and triggered \"invalid\n     +    reference\" error.  This error message did not contain enough\n     +    information to resolve the issue where the remote could not be\n     +    guessed.\n      \n     -    To resolve this issue, display a hint and a descriptive error message\n     -    and die immediately when multiple mathing branches are found.\n     +    To improve the situation, we display a hint and a descriptive error\n     +    message and die immediately when multiple matching branches are found.\n      \n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n\n\n builtin/worktree.c      | 23 ++++++++++++++++++++++-\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 24 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..b29c3a3755 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -64,6 +64,19 @@\n \t\"\\n\" \\\n \t\"    git worktree add --orphan %s\\n\")\n \n+#define WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT \\\n+\t_(\"Matched multiple remote tracking branches, you can list them by:\\n\" \\\n+\t\"\\n\" \\\n+\t\"    git branch -r --list \\\"*/%s\\\"\\n\" \\\n+\t\"\\n\" \\\n+\t\"If you meant to create a worktree from a remote tracking branch on,\\n\" \\\n+\t\"e.g. 'origin', you can do so by:\\n\" \\\n+\t\"\\n\" \\\n+\t\"    git worktree add -b %s %s origin/%s\\n\" \\\n+\t\"\\n\" \\\n+\t\"If you'd like to always prefer some remote, e.g. 'origin',\\n\" \\\n+\t\"consider setting checkout.defaultRemote=origin in your config.\")\n+\n static const char * const git_worktree_usage[] = {\n \tBUILTIN_WORKTREE_ADD_USAGE,\n \tBUILTIN_WORKTREE_LIST_USAGE,\n@@ -904,10 +917,18 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tint num_matches = 0;\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet)\n+\t\t\t\t\tadvise_if_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,\n+\t\t\t\t\t\t\t  WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT,\n+\t\t\t\t\t\t\t  branch, branch, path, branch);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n \t\t}\n \ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n\nbase-commit: 010afd3166ddc64c9863b1506f12cbcdda0d4ea1\n-- \ngitgitgadget\n"},{"id":"550301","messageId":"DCB65C9B-5E9F-4FF5-A8EE-27C9442119DC@gmail.com","threadId":"66141","inReplyTo":"CAF5D8-u23Z=f02vF1yAHGKRED8DY-v5=BNf7w-yY3vEDmJChDg@mail.gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-08-11T16:38:37Z","receivedAt":"2026-08-11T16:38:49Z","isPatch":true,"body":"\n> \n> Le 10 août 2026 à 17:36, Yoichi Nakayama <yoichi.nakayama@gmail.com> a écrit :\n> \n> ﻿On Mon, Aug 10, 2026 at 10:08 PM D. Ben Knoble <ben.knoble@gmail.com> wrote:\n>>> -static char *dwim_branch(const char *path, char **new_branch)\n>>> +static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n>>> {\n>>>        int n;\n>>>        int branch_exists;\n>>> @@ -781,8 +791,14 @@ static char *dwim_branch(const char *path, char **new_branch)\n>>> \n>>>        *new_branch = branchname;\n>>>        if (guess_remote) {\n>>> +               int num_matches = 0;\n>>>                struct object_id oid;\n>>> -               char *remote = unique_tracking_name(*new_branch, &oid, NULL);\n>>> +               char *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n>>> +               if (!opts->quiet && !remote && num_matches > 1) {\n>>> +                       if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n>>> +                               advise(_(message_advice_ambiguous_remote_tracking_branch));\n>>> +                       warning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n>>> +               }\n>>>                return remote;\n>>>        }\n>>>        return NULL;\n>> \n>> I suppose the extra warning won't hurt anyone's workflow :) so that's good.\n> \n> I removed the change (advise and warn) here in the latest patch. But I am still\n> wondering what I should do. I think a warning would be excessive if\n> there is no match,\n> but the user might want to know if there are multiple matches.\n> \n> Thanks,\n> --\n> Yoichi NAKAYAMA\n\nSorry if I was unclear: I don’t know what hits this particular code path offhand, and I don’t think adding the warning is wrong! I meant « as opposed to dying, a warning won’t break anyone but will let them know of potentially unintended consequences », maybe.\n\nI’m not particularly invested in how we signal ambiguity, but if we don’t have a documented policy for resolving it in a particular way then I agree we should signal it. "},{"id":"550400","messageId":"CAF5D8-vVA31Ci+=+kJk+Lk6E4KvgA319hm5d1HZXgmSdNSgwnQ@mail.gmail.com","threadId":"66141","inReplyTo":"DCB65C9B-5E9F-4FF5-A8EE-27C9442119DC@gmail.com","subject":"Re: [PATCH] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-12T13:14:34Z","receivedAt":"2026-08-12T13:14:46Z","isPatch":true,"body":"On Wed, Aug 12, 2026 at 1:38 AM Ben Knoble <ben.knoble@gmail.com> wrote:\n>\n>\n> >\n> > Le 10 août 2026 à 17:36, Yoichi Nakayama <yoichi.nakayama@gmail.com> a écrit :\n> >\n> > ﻿On Mon, Aug 10, 2026 at 10:08 PM D. Ben Knoble <ben.knoble@gmail.com> wrote:\n> >>> -static char *dwim_branch(const char *path, char **new_branch)\n> >>> +static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n> >>> {\n> >>>        int n;\n> >>>        int branch_exists;\n> >>> @@ -781,8 +791,14 @@ static char *dwim_branch(const char *path, char **new_branch)\n> >>>\n> >>>        *new_branch = branchname;\n> >>>        if (guess_remote) {\n> >>> +               int num_matches = 0;\n> >>>                struct object_id oid;\n> >>> -               char *remote = unique_tracking_name(*new_branch, &oid, NULL);\n> >>> +               char *remote = unique_tracking_name(*new_branch, &oid, &num_matches);\n> >>> +               if (!opts->quiet && !remote && num_matches > 1) {\n> >>> +                       if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> >>> +                               advise(_(message_advice_ambiguous_remote_tracking_branch));\n> >>> +                       warning(_(\"'%s' matched multiple (%d) remote tracking branches\\n\"), branchname, num_matches);\n> >>> +               }\n> >>>                return remote;\n> >>>        }\n> >>>        return NULL;\n> >>\n> >> I suppose the extra warning won't hurt anyone's workflow :) so that's good.\n> >\n> > I removed the change (advise and warn) here in the latest patch. But I am still\n> > wondering what I should do. I think a warning would be excessive if\n> > there is no match,\n> > but the user might want to know if there are multiple matches.\n> >\n> > Thanks,\n> > --\n> > Yoichi NAKAYAMA\n>\n> Sorry if I was unclear: I don’t know what hits this particular code path offhand, and I don’t think adding the warning is wrong! I meant « as opposed to dying, a warning won’t break anyone but will let them know of potentially unintended consequences », maybe.\n>\n> I’m not particularly invested in how we signal ambiguity, but if we don’t have a documented policy for resolving it in a particular way then I agree we should signal it.\n\nThank you for the additional information. I think the intention behind your\ncomment was clear. The reason I was wondering was simply that I hadn't yet\nfound a clear direction for what I should do.\n\nThe `git worktree add --guess-remote ../foo` command (without <commit-ish>) is\ndesigned to successfully create the worktree even if the guess fails.\nIf we are to\nissue a warning and advice, we need to indicate what to do next, given that the\nworktree and the branch has already been created. It would be something like\n    git -C ../foo reset –hard origin/foo\n    git -C ../foo branch –set-upstream-to=origin/foo\nbut they are too complicated. So I now think it is a valid choice to\ninterpret the\nbehavior as normal and refrain from issuing a warning or advice.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550444","messageId":"xmqqjypvw3cg.fsf@gitster.g","threadId":"66141","inReplyTo":"CAF5D8-uCjA-MFtdBCa0+5PDb-LFJ7JJ0yK1AtuWCKEN+tKQa_Q@mail.gmail.com","subject":"Re: [PATCH v3] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-12T19:22:55Z","receivedAt":"2026-08-12T19:22:58Z","isPatch":true,"body":"Yoichi Nakayama <yoichi.nakayama@gmail.com> writes:\n\n>> Instead of throwing the problem back to the user with four extra\n>> lines of message telling them how to run 'git branch', I would have\n>> expected this patch to teach unique_tracking_name() to optionally\n>> return the list of remotes with that branch name, and to use that\n>> result in this message.  However, if the goal is simply to provide\n>> something better than 'invalid reference', we do not even need to\n>> go that far.  Just stating that branch 'y' appears on multiple\n>> remotes and asking them to clarify which one they mean might be a\n>> sufficient improvement.\n>\n> Extending `unique_tracking_name()` would also affect the implementation\n> in `checkout.c`, and since the goal here is to improve the messages\n> (making them as helpful as those in `checkout`), I will hold off on doing\n> that for now.\n\nSorry but I do not quite understand this logic.\n\nGiving unique_tracking_name() the optional ability to report which\nremotes have a branch with the given name does not have to affect\nother callers of the function at all; that is the definition of a\nnew feature being \"optional.\"\n\nFurthermore, the goal of improving these messages falls short if we\nwithhold the list of remotes the user could have meant, which we are\nalready computing internally to decide that the original request is\nambiguous.\n\n>> Could the original request be aiming to create a new worktree with\n>> the HEAD detached at the commit pointed at by the remote-tracking\n>> branch, instead of creating a local branch forked from it?  I am\n>> just wondering if \"-b %s\" is too specific to one possible\n>> interpretation that may contradict to what the user actually wanted\n>> to do.\n>\n> If the user is aiming to create a new worktree with the HEAD detached,\n> one would specify a fully qualified branch name like origin/bar-topic,\n> starting with a remote name.\n\nI am not sure about this part, either.  After all, we are trying to\nhelp a user who made a mistake composing their command-line\narguments.  If they specify a fully qualified branch name like\n'origin/bar-topic' (regardless of whether they want to create a\ndetached 'HEAD' or not), they would not hit the 'saying bar-topic\nalone is ambiguous' error path, would they?\n\nHere is a patch to show what I mean.  To 'improve the message for an\nambiguous remote branch name' for the 'checkout' command, we can add\nthis optional feature to the unique_tracking_name() function and we\ncan do so without affecting the 'worktree add' command.\n\nRegardless of whether we modify unique_tracking_name(), I think\nrefactoring be_explicit() is worth doing, as the original code in\nparse_remote_branch() was badly misindented.\n\n\n builtin/checkout.c | 74 ++++++++++++++++++++++++++++++++----------------------\n builtin/worktree.c |  4 +--\n checkout.c         | 14 +++++++++--\n checkout.h         |  5 +++-\n 4 files changed, 62 insertions(+), 35 deletions(-)\n\ndiff --git c/builtin/checkout.c w/builtin/checkout.c\nindex 55e3a89a85..8d61973a11 100644\n--- c/builtin/checkout.c\n+++ w/builtin/checkout.c\n@@ -1343,13 +1343,50 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n+static void be_explicit(enum checkout_command which_command,\n+\t\t\tconst struct string_list *matched_remote_names)\n+{\n+\tconst char *cmdname;\n+\tstruct string_list_item *item;\n+\n+\tswitch (which_command) {\n+\tcase CHECKOUT_CHECKOUT:\n+\t\tcmdname = \"checkout\";\n+\t\tbreak;\n+\tcase CHECKOUT_SWITCH:\n+\t\tcmdname = \"switch\";\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n+\t\t     which_command);\n+\t\tbreak;\n+\t}\n+\n+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t \"checkout.defaultRemote=origin in your config.\"),\n+\t       cmdname);\n+}\n+\n static char *parse_remote_branch(const char *arg,\n \t\t\t\t struct object_id *rev,\n \t\t\t\t int could_be_checkout_paths,\n \t\t\t\t enum checkout_command which_command)\n {\n \tint num_matches = 0;\n-\tchar *remote = unique_tracking_name(arg, rev, &num_matches);\n+\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\tchar *remote = unique_tracking_name(arg, rev, &num_matches,\n+\t\t\t\t\t    &matched_remote_names);\n \n \tif (remote && could_be_checkout_paths) {\n \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n@@ -1358,37 +1395,14 @@ static char *parse_remote_branch(const char *arg,\n \t}\n \n \tif (!remote && num_matches > 1) {\n-\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n-\t\t    const char *cmdname;\n-\n-\t\t    switch (which_command) {\n-\t\t    case CHECKOUT_CHECKOUT:\n-\t\t\t    cmdname = \"checkout\";\n-\t\t\t    break;\n-\t\t    case CHECKOUT_SWITCH:\n-\t\t\t    cmdname = \"switch\";\n-\t\t\t    break;\n-\t\t    default:\n-\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n-\t\t\t\twhich_command);\n-\t\t\t    break;\n-\t\t    }\n-\n-\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n-\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"    git %s --track origin/<name>\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n-\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n-\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n-\t\t\t   cmdname);\n-\t    }\n-\n-\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n-\t\targ, num_matches);\n+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\tbe_explicit(which_command, &matched_remote_names);\n+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t    arg, num_matches);\n \t}\n \n+\tstring_list_clear(&matched_remote_names, 0);\n+\n \treturn remote;\n }\n \ndiff --git c/builtin/worktree.c w/builtin/worktree.c\nindex 654d27c3e1..22c8e5e131 100644\n--- c/builtin/worktree.c\n+++ w/builtin/worktree.c\n@@ -782,7 +782,7 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -904,7 +904,7 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\ndiff --git c/checkout.c w/checkout.c\nindex 1588b116ee..2806b783ec 100644\n--- c/checkout.c\n+++ w/checkout.c\n@@ -8,6 +8,7 @@\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n@@ -17,6 +18,7 @@ struct tracking_name_data {\n \tconst char *default_remote;\n \tchar *default_dst_ref;\n \tstruct object_id *default_dst_oid;\n+\tstruct string_list **remote_names;\n };\n \n #define TRACKING_NAME_DATA_INIT { 0 }\n@@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\toidcpy(dst, cb->dst_oid);\n \t\tcb->default_dst_oid = dst;\n \t}\n+\tif (cb->remote_names)\n+\t\tstring_list_append(*cb->remote_names, remote->name);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -48,14 +52,20 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n }\n \n char *unique_tracking_name(const char *name, struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched)\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tconst char *default_remote = NULL;\n-\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n+\n+\tif (!repo_config_get_string_tmp(the_repository,\n+\t\t\t\t\t\"checkout.defaultremote\",\n+\t\t\t\t\t&default_remote))\n \t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n+\tif (dwim_remote_names)\n+\t\tcb_data.remote_names = &dwim_remote_names;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\ndiff --git c/checkout.h w/checkout.h\nindex 55920e7aeb..0b185a0fc9 100644\n--- c/checkout.h\n+++ w/checkout.h\n@@ -3,6 +3,8 @@\n \n #include \"hash.h\"\n \n+struct string_list;\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n@@ -10,6 +12,7 @@\n  */\n char *unique_tracking_name(const char *name,\n \t\t\t   struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched);\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names);\n \n #endif /* CHECKOUT_H */\n\n\n"},{"id":"550654","messageId":"CAF5D8-tfAfjNONh8dbqxmF+kzzaQ4rSQL94DK2=TLd9_HV9oaA@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqjypvw3cg.fsf@gitster.g","subject":"Re: [PATCH v3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-15T04:36:25Z","receivedAt":"2026-08-15T04:36:38Z","isPatch":true,"body":"On Thu, Aug 13, 2026 at 4:22 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Yoichi Nakayama <yoichi.nakayama@gmail.com> writes:\n>\n> >> Instead of throwing the problem back to the user with four extra\n> >> lines of message telling them how to run 'git branch', I would have\n> >> expected this patch to teach unique_tracking_name() to optionally\n> >> return the list of remotes with that branch name, and to use that\n> >> result in this message.  However, if the goal is simply to provide\n> >> something better than 'invalid reference', we do not even need to\n> >> go that far.  Just stating that branch 'y' appears on multiple\n> >> remotes and asking them to clarify which one they mean might be a\n> >> sufficient improvement.\n> >\n> > Extending `unique_tracking_name()` would also affect the implementation\n> > in `checkout.c`, and since the goal here is to improve the messages\n> > (making them as helpful as those in `checkout`), I will hold off on doing\n> > that for now.\n>\n> Sorry but I do not quite understand this logic.\n>\n> Giving unique_tracking_name() the optional ability to report which\n> remotes have a branch with the given name does not have to affect\n> other callers of the function at all; that is the definition of a\n> new feature being \"optional.\"\n>\n> Furthermore, the goal of improving these messages falls short if we\n> withhold the list of remotes the user could have meant, which we are\n> already computing internally to decide that the original request is\n> ambiguous.\n\nThat is certainly true.\nI think the modification to `unique_tracking_name()` that you shared is good.\n\n> >> Could the original request be aiming to create a new worktree with\n> >> the HEAD detached at the commit pointed at by the remote-tracking\n> >> branch, instead of creating a local branch forked from it?  I am\n> >> just wondering if \"-b %s\" is too specific to one possible\n> >> interpretation that may contradict to what the user actually wanted\n> >> to do.\n> >\n> > If the user is aiming to create a new worktree with the HEAD detached,\n> > one would specify a fully qualified branch name like origin/bar-topic,\n> > starting with a remote name.\n>\n> I am not sure about this part, either.  After all, we are trying to\n> help a user who made a mistake composing their command-line\n> arguments.  If they specify a fully qualified branch name like\n> 'origin/bar-topic' (regardless of whether they want to create a\n> detached 'HEAD' or not), they would not hit the 'saying bar-topic\n> alone is ambiguous' error path, would they?\n\nIf the user specifies a fully qualified branch name like\n    git worktree add ../foo-dir origin/bar-topic\nthe 2nd argument 'origin/bar-topic' will be resolved by\n    commit = lookup_commit_reference_by_name(branch);\nbefore calling unique_tracking_name(), so the ambiguous warning\nwon't occur.\n\nIn a worst-case scenario, a user might make a typo in the 2nd argument (e.g.\norigin/bar-topik) that fails to match a local ref (e.g.\nrefs/remotes/origin/bar-topic)\nbut accidentally matches multiple remote branch names (e.g. branches related to\nrefs/remotes/remote1/origin/bar-topik and\nrefs/remotes/remote2/origin/bar-topik),\nthen it could cause an issue. However, I think this is an edge case that doesn't\nneed to be considered.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550809","messageId":"pull.2197.v5.git.1787143859.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v5 0/2] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-19T12:50:56Z","receivedAt":"2026-08-19T12:51:03Z","isPatch":true,"body":"'git worktree add ../foo-dir bar-topic' fails to dwim when there are\nmultiple remote branches with name `bar-topic'. But it doesn't display\nmeaningful message as 'git checkout bar-topic' does under the same\nsituation.\n\nWe improve this by adding advice and modify the error message for worktree\nadd.\n\nBy Junio's suggestion, we include matched remote names in the advice. It is\napplied to checkout, too.\n\nThe changes to 'checkout' are almost identical to what Junio proposed; I\nhave made minor adjustments to use the specified branch name. I'm not sure\nhow to handle the \"Author\" field in this case, so I've set it to myself for\nnow, but I'll correct it if that's not appropriate.\n\nYoichi NAKAYAMA (2):\n  checkout: improve message for ambiguous remote branch name\n  worktree add: improve message for ambiguous remote branch name\n\n builtin/checkout.c      | 75 ++++++++++++++++++++++++-----------------\n builtin/worktree.c      | 36 ++++++++++++++++++--\n checkout.c              | 14 ++++++--\n checkout.h              |  5 ++-\n t/t2400-worktree-add.sh |  4 +--\n 5 files changed, 96 insertions(+), 38 deletions(-)\n\n\nbase-commit: dea0ea3582e6980ddbc1173cc8e3e9f9db91cde0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v5\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v5\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v4:\n\n -:  ---------- > 1:  b838fdabb7 checkout: improve message for ambiguous remote branch name\n 1:  f7c413b588 ! 2:  777862235e worktree add: improve message for ambiguous remote branch name\n     @@ Commit message\n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n       ## builtin/worktree.c ##\n     -@@\n     - \t\"\\n\" \\\n     - \t\"    git worktree add --orphan %s\\n\")\n     +@@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch)\n     + \treturn NULL;\n     + }\n       \n     -+#define WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT \\\n     -+\t_(\"Matched multiple remote tracking branches, you can list them by:\\n\" \\\n     -+\t\"\\n\" \\\n     -+\t\"    git branch -r --list \\\"*/%s\\\"\\n\" \\\n     -+\t\"\\n\" \\\n     -+\t\"If you meant to create a worktree from a remote tracking branch on,\\n\" \\\n     -+\t\"e.g. 'origin', you can do so by:\\n\" \\\n     -+\t\"\\n\" \\\n     -+\t\"    git worktree add -b %s %s origin/%s\\n\" \\\n     -+\t\"\\n\" \\\n     -+\t\"If you'd like to always prefer some remote, e.g. 'origin',\\n\" \\\n     -+\t\"consider setting checkout.defaultRemote=origin in your config.\")\n     ++static void advise_ambiguous_remote(const char *path, const char *branch,\n     ++\t\t\t\t    const struct string_list *matched_remote_names)\n     ++{\n     ++\tstruct string_list_item *item;\n      +\n     - static const char * const git_worktree_usage[] = {\n     - \tBUILTIN_WORKTREE_ADD_USAGE,\n     - \tBUILTIN_WORKTREE_LIST_USAGE,\n     ++\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n     ++\tfor_each_string_list_item(item, matched_remote_names) {\n     ++\t\tadvise(_(\"  %s\"), item->string);\n     ++\t}\n     ++\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n     ++\t\t \"<remote>, you can do so by:\\n\"\n     ++\t\t \"\\n\"\n     ++\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n     ++\t\t \"\\n\"\n     ++\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n     ++\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n     ++\t       branch, path, branch);\n     ++}\n     ++\n     + static int add(int ac, const char **av, const char *prefix,\n     + \t       struct repository *repo UNUSED)\n     + {\n      @@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n     + \t} else if (ac == 2) {\n     + \t\tstruct object_id oid;\n     + \t\tstruct commit *commit;\n     +-\t\tchar *remote;\n       \n       \t\tcommit = lookup_commit_reference_by_name(branch);\n       \t\tif (!commit) {\n     --\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n     +-\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n     ++\t\t\tchar *remote;\n      +\t\t\tint num_matches = 0;\n     -+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches);\n     ++\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n     ++\n     ++\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n     ++\t\t\t\t\t\t      &matched_remote_names);\n       \t\t\tif (remote) {\n       \t\t\t\tnew_branch = branch;\n       \t\t\t\tbranch = new_branch_to_free = remote;\n      +\t\t\t} else if (num_matches > 1) {\n     -+\t\t\t\tif (!opts.quiet)\n     -+\t\t\t\t\tadvise_if_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,\n     -+\t\t\t\t\t\t\t  WORKTREE_ADD_AMBIGUOUS_REMOTE_BRANCH_NAME_HINT_TEXT,\n     -+\t\t\t\t\t\t\t  branch, branch, path, branch);\n     ++\t\t\t\tif (!opts.quiet &&\n     ++\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n     ++\t\t\t\t\tadvise_ambiguous_remote(path, branch, &matched_remote_names);\n      +\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n      +\t\t\t\t    branch, num_matches);\n       \t\t\t}\n     ++\t\t\tstring_list_clear(&matched_remote_names, 0);\n       \t\t}\n       \n     + \t\tif (!strcmp(branch, \"HEAD\"))\n      \n       ## t/t2400-worktree-add.sh ##\n      @@ t/t2400-worktree-add.sh: test_expect_success '\"add\" <path> <branch> dwims' '\n\n-- \ngitgitgadget\n"},{"id":"550810","messageId":"b838fdabb7cd0d044294d54fd30cb51f040505ad.1787143859.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v5.git.1787143859.gitgitgadget@gmail.com","subject":"[PATCH v5 1/2] checkout: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-19T12:50:57Z","receivedAt":"2026-08-19T12:51:03Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git checkout bar-topic' command that does not\nexactly say which remote they want to work with, and there is no local\nbranch named bar-topic, we try to guess which remote by passing\nbar-topic then create a new branch named bar-topic which tracks the\nremote branch.\n\nIf multiple remotes have a branch named bar-topic, we cannot determine\na single specific remote. Therefore, we provide information that the\nuser can utilize to resolve the issue.\n\nTo make the advice more feasible, we will provide matched remote names\nfor the specified branch name.\n\nTo achive that, we add an optional feature to the\n`unique_tracking_name()` function that allows the matched remote name\nto be exposed to the caller.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 75 +++++++++++++++++++++++++++-------------------\n builtin/worktree.c |  4 +--\n checkout.c         | 14 +++++++--\n checkout.h         |  5 +++-\n 4 files changed, 63 insertions(+), 35 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 55e3a89a85..a2749352e6 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,13 +1343,51 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n+static void be_explicit(const char *branch,\n+\t\t\tenum checkout_command which_command,\n+\t\t\tconst struct string_list *matched_remote_names)\n+{\n+\tconst char *cmdname;\n+\tstruct string_list_item *item;\n+\n+\tswitch (which_command) {\n+\tcase CHECKOUT_CHECKOUT:\n+\t\tcmdname = \"checkout\";\n+\t\tbreak;\n+\tcase CHECKOUT_SWITCH:\n+\t\tcmdname = \"switch\";\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n+\t\t     which_command);\n+\t\tbreak;\n+\t}\n+\n+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git %s --track <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t \"checkout.defaultRemote=origin in your config.\"),\n+\t       cmdname, branch);\n+}\n+\n static char *parse_remote_branch(const char *arg,\n \t\t\t\t struct object_id *rev,\n \t\t\t\t int could_be_checkout_paths,\n \t\t\t\t enum checkout_command which_command)\n {\n \tint num_matches = 0;\n-\tchar *remote = unique_tracking_name(arg, rev, &num_matches);\n+\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\tchar *remote = unique_tracking_name(arg, rev, &num_matches,\n+\t\t\t\t\t    &matched_remote_names);\n \n \tif (remote && could_be_checkout_paths) {\n \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n@@ -1358,37 +1396,14 @@ static char *parse_remote_branch(const char *arg,\n \t}\n \n \tif (!remote && num_matches > 1) {\n-\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n-\t\t    const char *cmdname;\n-\n-\t\t    switch (which_command) {\n-\t\t    case CHECKOUT_CHECKOUT:\n-\t\t\t    cmdname = \"checkout\";\n-\t\t\t    break;\n-\t\t    case CHECKOUT_SWITCH:\n-\t\t\t    cmdname = \"switch\";\n-\t\t\t    break;\n-\t\t    default:\n-\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n-\t\t\t\twhich_command);\n-\t\t\t    break;\n-\t\t    }\n-\n-\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n-\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"    git %s --track origin/<name>\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n-\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n-\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n-\t\t\t   cmdname);\n-\t    }\n-\n-\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n-\t\targ, num_matches);\n+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\tbe_explicit(arg, which_command, &matched_remote_names);\n+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t    arg, num_matches);\n \t}\n \n+\tstring_list_clear(&matched_remote_names, 0);\n+\n \treturn remote;\n }\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..22c8e5e131 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -782,7 +782,7 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -904,7 +904,7 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 1588b116ee..2806b783ec 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -8,6 +8,7 @@\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n@@ -17,6 +18,7 @@ struct tracking_name_data {\n \tconst char *default_remote;\n \tchar *default_dst_ref;\n \tstruct object_id *default_dst_oid;\n+\tstruct string_list **remote_names;\n };\n \n #define TRACKING_NAME_DATA_INIT { 0 }\n@@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\toidcpy(dst, cb->dst_oid);\n \t\tcb->default_dst_oid = dst;\n \t}\n+\tif (cb->remote_names)\n+\t\tstring_list_append(*cb->remote_names, remote->name);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -48,14 +52,20 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n }\n \n char *unique_tracking_name(const char *name, struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched)\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tconst char *default_remote = NULL;\n-\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n+\n+\tif (!repo_config_get_string_tmp(the_repository,\n+\t\t\t\t\t\"checkout.defaultremote\",\n+\t\t\t\t\t&default_remote))\n \t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n+\tif (dwim_remote_names)\n+\t\tcb_data.remote_names = &dwim_remote_names;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\ndiff --git a/checkout.h b/checkout.h\nindex 55920e7aeb..0b185a0fc9 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -3,6 +3,8 @@\n \n #include \"hash.h\"\n \n+struct string_list;\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n@@ -10,6 +12,7 @@\n  */\n char *unique_tracking_name(const char *name,\n \t\t\t   struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched);\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names);\n \n #endif /* CHECKOUT_H */\n-- \ngitgitgadget\n\n"},{"id":"550811","messageId":"777862235e884f4fa704c4feb99df17b1da83038.1787143859.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v5.git.1787143859.gitgitgadget@gmail.com","subject":"[PATCH v5 2/2] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-19T12:50:58Z","receivedAt":"2026-08-19T12:51:05Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add ../foo-dir bar-topic' command\nthat does not exactly say which remote they want to work with, and\nthere is no local branch named bar-topic, we try to guess which remote\nby passing bar-topic then create a new branch named bar-topic which\ntracks the remote branch.\n\nIf there are multiple remotes that have branch named bar-topic, we\nsilently gave up, leaving the variable 'branch' intact.  Then we\nentered the conditional clause 'if (!opts.orphan &&\n!lookup_commit_reference_by_name(branch))' and triggered \"invalid\nreference\" error.  This error message did not contain enough\ninformation to resolve the issue where the remote could not be\nguessed.\n\nTo improve the situation, we display a hint and a descriptive error\nmessage and die immediately when multiple matching branches are found.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/worktree.c      | 34 ++++++++++++++++++++++++++++++++--\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 34 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 22c8e5e131..30d7a8c340 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -788,6 +788,25 @@ static char *dwim_branch(const char *path, char **new_branch)\n \treturn NULL;\n }\n \n+static void advise_ambiguous_remote(const char *path, const char *branch,\n+\t\t\t\t    const struct string_list *matched_remote_names)\n+{\n+\tstruct string_list_item *item;\n+\n+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n+\t\t \"<remote>, you can do so by:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n+\t       branch, path, branch);\n+}\n+\n static int add(int ac, const char **av, const char *prefix,\n \t       struct repository *repo UNUSED)\n {\n@@ -900,15 +919,26 @@ static int add(int ac, const char **av, const char *prefix,\n \t} else if (ac == 2) {\n \t\tstruct object_id oid;\n \t\tstruct commit *commit;\n-\t\tchar *remote;\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n+\t\t\tchar *remote;\n+\t\t\tint num_matches = 0;\n+\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet &&\n+\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\t\tadvise_ambiguous_remote(path, branch, &matched_remote_names);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n+\t\t\tstring_list_clear(&matched_remote_names, 0);\n \t\t}\n \n \t\tif (!strcmp(branch, \"HEAD\"))\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n-- \ngitgitgadget\n"},{"id":"550855","messageId":"CALnO6CCQppTnw1Y-6ibmcko0dM3hy8riPanD9BNyb+MW8a5nAw@mail.gmail.com","threadId":"66141","inReplyTo":"b838fdabb7cd0d044294d54fd30cb51f040505ad.1787143859.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v5 1/2] checkout: improve message for ambiguous remote branch name","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2026-08-19T22:54:02Z","receivedAt":"2026-08-19T22:54:15Z","isPatch":true,"body":"On Wed, Aug 19, 2026 at 8:51 AM Yoichi NAKAYAMA via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n>\n> When the user runs 'git checkout bar-topic' command that does not\n> exactly say which remote they want to work with, and there is no local\n> branch named bar-topic, we try to guess which remote by passing\n> bar-topic then create a new branch named bar-topic which tracks the\n> remote branch.\n>\n> If multiple remotes have a branch named bar-topic, we cannot determine\n> a single specific remote. Therefore, we provide information that the\n> user can utilize to resolve the issue.\n>\n> To make the advice more feasible, we will provide matched remote names\n> for the specified branch name.\n>\n> To achive that, we add an optional feature to the\n> `unique_tracking_name()` function that allows the matched remote name\n> to be exposed to the caller.\n>\n> Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n> ---\n>  builtin/checkout.c | 75 +++++++++++++++++++++++++++-------------------\n>  builtin/worktree.c |  4 +--\n>  checkout.c         | 14 +++++++--\n>  checkout.h         |  5 +++-\n>  4 files changed, 63 insertions(+), 35 deletions(-)\n>\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 55e3a89a85..a2749352e6 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -1343,13 +1343,51 @@ enum checkout_command {\n>         CHECKOUT_RESTORE = 3,\n>  };\n>\n> +static void be_explicit(const char *branch,\n\nBe explicit about what? Reading below, a better name might be\n\"advise_ambiguous_remote_branch_name\" or something, idk.\n\n> +                       enum checkout_command which_command,\n> +                       const struct string_list *matched_remote_names)\n> +{\n> +       const char *cmdname;\n> +       struct string_list_item *item;\n> +\n> +       switch (which_command) {\n> +       case CHECKOUT_CHECKOUT:\n> +               cmdname = \"checkout\";\n> +               break;\n> +       case CHECKOUT_SWITCH:\n> +               cmdname = \"switch\";\n> +               break;\n> +       default:\n> +               BUG(\"command <%d> should not reach parse_remote_branch\",\n> +                    which_command);\n> +               break;\n> +       }\n> +\n> +       advise(_(\"Branches with the same name appears in multiple remotes:\"));\n> +       for_each_string_list_item(item, matched_remote_names) {\n> +               advise(_(\"  %s\"), item->string);\n> +       }\n> +       advise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n> +                \"you can do so by fully qualifying the name with the --track option:\\n\"\n> +                \"\\n\"\n> +                \"    git %s --track <remote>/%s\\n\"\n> +                \"\\n\"\n> +                \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n> +                \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n> +                \"checkout.defaultRemote=origin in your config.\"),\n> +              cmdname, branch);\n> +}\n> +\n\nI think it's possible this refactor is a bit distracting from the\noverall goal of the patch, though I don't think extracting the\nfunction is a bad thing. Maybe split the steps up into\n\n- mechanical refactoring (no behavior change)\n- changes to improve the message (easier to see the diff)\n\n? Just my 2 cents.\n\n>  static char *parse_remote_branch(const char *arg,\n>                                  struct object_id *rev,\n>                                  int could_be_checkout_paths,\n>                                  enum checkout_command which_command)\n>  {\n>         int num_matches = 0;\n> -       char *remote = unique_tracking_name(arg, rev, &num_matches);\n> +       struct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n> +\n> +       char *remote = unique_tracking_name(arg, rev, &num_matches,\n> +                                           &matched_remote_names);\n>\n>         if (remote && could_be_checkout_paths) {\n>                 die(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n> @@ -1358,37 +1396,14 @@ static char *parse_remote_branch(const char *arg,\n>         }\n>\n>         if (!remote && num_matches > 1) {\n> -           if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n> -                   const char *cmdname;\n> -\n> -                   switch (which_command) {\n> -                   case CHECKOUT_CHECKOUT:\n> -                           cmdname = \"checkout\";\n> -                           break;\n> -                   case CHECKOUT_SWITCH:\n> -                           cmdname = \"switch\";\n> -                           break;\n> -                   default:\n> -                           BUG(\"command <%d> should not reach parse_remote_branch\",\n> -                               which_command);\n> -                           break;\n> -                   }\n> -\n> -                   advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n> -                            \"you can do so by fully qualifying the name with the --track option:\\n\"\n> -                            \"\\n\"\n> -                            \"    git %s --track origin/<name>\\n\"\n> -                            \"\\n\"\n> -                            \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n> -                            \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n> -                            \"checkout.defaultRemote=origin in your config.\"),\n> -                          cmdname);\n> -           }\n> -\n> -           die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n> -               arg, num_matches);\n> +               if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> +                       be_explicit(arg, which_command, &matched_remote_names);\n> +               die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n> +                   arg, num_matches);\n>         }\n>\n> +       string_list_clear(&matched_remote_names, 0);\n> +\n>         return remote;\n>  }\n\n[rest of diff snipped]\n"},{"id":"550858","messageId":"xmqqjyplttzr.fsf@gitster.g","threadId":"66141","inReplyTo":"CALnO6CCQppTnw1Y-6ibmcko0dM3hy8riPanD9BNyb+MW8a5nAw@mail.gmail.com","subject":"Re: [PATCH v5 1/2] checkout: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-20T02:18:16Z","receivedAt":"2026-08-20T02:18:19Z","isPatch":true,"body":"\"D. Ben Knoble\" <ben.knoble@gmail.com> writes:\n\n>> +static void be_explicit(const char *branch,\n>\n> Be explicit about what? Reading below, a better name might be\n> \"advise_ambiguous_remote_branch_name\" or something, idk.\n\nIt stands for \"tell the user to be more explicit\".  I agree with you\nthat the refactoring should be done as a separate step, on top of\nwhich we should add the new feature, i.e., \"give list of possible\ncandidates\", as a separate step.\n"},{"id":"550914","messageId":"CAF5D8-u+ZxTrmrGDzHvdPS+jDe+bR6ZAYNq5KZ5R-MRpjWedgg@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqjyplttzr.fsf@gitster.g","subject":"Re: [PATCH v5 1/2] checkout: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-20T15:41:49Z","receivedAt":"2026-08-20T15:42:01Z","isPatch":true,"body":"On Thu, Aug 20, 2026 at 11:18 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"D. Ben Knoble\" <ben.knoble@gmail.com> writes:\n>\n> >> +static void be_explicit(const char *branch,\n> >\n> > Be explicit about what? Reading below, a better name might be\n> > \"advise_ambiguous_remote_branch_name\" or something, idk.\n>\n> It stands for \"tell the user to be more explicit\".  I agree with you\n> that the refactoring should be done as a separate step, on top of\n> which we should add the new feature, i.e., \"give list of possible\n> candidates\", as a separate step.\n\nI think it's a good idea to separate the steps, so I'll split the commits.\nI am considering \"advise_disambiguating_remotes\" as a better function name.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"550942","messageId":"pull.2197.v6.git.1787259838.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v6 0/3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-20T21:03:55Z","receivedAt":"2026-08-20T21:04:00Z","isPatch":true,"body":"'git worktree add ../foo-dir bar-topic' fails to dwim when there are\nmultiple remote branches with name `bar-topic'. But it doesn't display\nmeaningful message as 'git checkout bar-topic' does under the same\nsituation.\n\nWe improve this by adding advice and modify the error message for worktree\nadd.\n\nBy Junio's suggestion, we include matched remote names in the advice. It is\napplied to checkout, too.\n\nThe changes to 'checkout' are almost identical to what Junio proposed; I\nhave made minor adjustments to use the specified branch name. I'm not sure\nhow to handle the \"Author\" field in this case, so I've set it to myself for\nnow, but I'll correct it if that's not appropriate.\n\nYoichi NAKAYAMA (3):\n  checkout: extract function to display advice for ambiguous remotes\n  checkout: improve message for ambiguous remote branch name\n  worktree add: improve message for ambiguous remote branch name\n\n builtin/checkout.c      | 76 +++++++++++++++++++++++++----------------\n builtin/worktree.c      | 37 ++++++++++++++++++--\n checkout.c              | 14 ++++++--\n checkout.h              |  5 ++-\n t/t2400-worktree-add.sh |  4 +--\n 5 files changed, 98 insertions(+), 38 deletions(-)\n\n\nbase-commit: dea0ea3582e6980ddbc1173cc8e3e9f9db91cde0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v6\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v6\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v5:\n\n -:  ---------- > 1:  e3f7d88520 checkout: extract function to display advice for ambiguous remotes\n 1:  b838fdabb7 ! 2:  97e99ae31e checkout: improve message for ambiguous remote branch name\n     @@ Commit message\n          To make the advice more feasible, we will provide matched remote names\n          for the specified branch name.\n      \n     -    To achive that, we add an optional feature to the\n     +    To achieve that, we add an optional feature to the\n          `unique_tracking_name()` function that allows the matched remote name\n          to be exposed to the caller.\n      \n     @@ builtin/checkout.c: enum checkout_command {\n       \tCHECKOUT_RESTORE = 3,\n       };\n       \n     -+static void be_explicit(const char *branch,\n     -+\t\t\tenum checkout_command which_command,\n     -+\t\t\tconst struct string_list *matched_remote_names)\n     -+{\n     -+\tconst char *cmdname;\n     +-static void advice_disambiguating_remotes(enum checkout_command which_command)\n     ++static void advise_disambiguating_remotes(enum checkout_command which_command,\n     ++\t\t\t\t\t  const char *branch,\n     ++\t\t\t\t\t  const struct string_list *matched_remote_names)\n     + {\n     + \tconst char *cmdname;\n      +\tstruct string_list_item *item;\n     -+\n     -+\tswitch (which_command) {\n     -+\tcase CHECKOUT_CHECKOUT:\n     -+\t\tcmdname = \"checkout\";\n     -+\t\tbreak;\n     -+\tcase CHECKOUT_SWITCH:\n     -+\t\tcmdname = \"switch\";\n     -+\t\tbreak;\n     -+\tdefault:\n     -+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n     -+\t\t     which_command);\n     -+\t\tbreak;\n     -+\t}\n     -+\n     + \n     + \tswitch (which_command) {\n     + \tcase CHECKOUT_CHECKOUT:\n     +@@ builtin/checkout.c: static void advice_disambiguating_remotes(enum checkout_command which_command)\n     + \t\tbreak;\n     + \t}\n     + \n     +-\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n      +\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n      +\tfor_each_string_list_item(item, matched_remote_names) {\n      +\t\tadvise(_(\"  %s\"), item->string);\n      +\t}\n      +\tadvise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n     -+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n     -+\t\t \"\\n\"\n     + \t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n     + \t\t \"\\n\"\n     +-\t\t \"    git %s --track origin/<name>\\n\"\n      +\t\t \"    git %s --track <remote>/%s\\n\"\n     -+\t\t \"\\n\"\n     + \t\t \"\\n\"\n     +-\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n      +\t\t \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n     -+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n     -+\t\t \"checkout.defaultRemote=origin in your config.\"),\n     + \t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n     + \t\t \"checkout.defaultRemote=origin in your config.\"),\n     +-\t       cmdname);\n      +\t       cmdname, branch);\n     -+}\n     -+\n     + }\n     + \n       static char *parse_remote_branch(const char *arg,\n     - \t\t\t\t struct object_id *rev,\n     - \t\t\t\t int could_be_checkout_paths,\n     +@@ builtin/checkout.c: static char *parse_remote_branch(const char *arg,\n       \t\t\t\t enum checkout_command which_command)\n       {\n       \tint num_matches = 0;\n     @@ builtin/checkout.c: enum checkout_command {\n       \tif (remote && could_be_checkout_paths) {\n       \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n      @@ builtin/checkout.c: static char *parse_remote_branch(const char *arg,\n     - \t}\n       \n       \tif (!remote && num_matches > 1) {\n     --\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n     --\t\t    const char *cmdname;\n     --\n     --\t\t    switch (which_command) {\n     --\t\t    case CHECKOUT_CHECKOUT:\n     --\t\t\t    cmdname = \"checkout\";\n     --\t\t\t    break;\n     --\t\t    case CHECKOUT_SWITCH:\n     --\t\t\t    cmdname = \"switch\";\n     --\t\t\t    break;\n     --\t\t    default:\n     --\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n     --\t\t\t\twhich_command);\n     --\t\t\t    break;\n     --\t\t    }\n     --\n     --\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n     --\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n     --\t\t\t     \"\\n\"\n     --\t\t\t     \"    git %s --track origin/<name>\\n\"\n     --\t\t\t     \"\\n\"\n     --\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n     --\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n     --\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n     --\t\t\t   cmdname);\n     --\t    }\n     + \t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n     +-\t\t\tadvice_disambiguating_remotes(which_command);\n      -\n     --\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n     --\t\targ, num_matches);\n     -+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n     -+\t\t\tbe_explicit(arg, which_command, &matched_remote_names);\n     -+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n     -+\t\t    arg, num_matches);\n     ++\t\t\tadvise_disambiguating_remotes(which_command, arg,\n     ++\t\t\t\t\t\t      &matched_remote_names);\n     + \t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n     + \t\t    arg, num_matches);\n       \t}\n       \n      +\tstring_list_clear(&matched_remote_names, 0);\n 2:  777862235e ! 3:  dcb84a69a6 worktree add: improve message for ambiguous remote branch name\n     @@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch\n       \treturn NULL;\n       }\n       \n     -+static void advise_ambiguous_remote(const char *path, const char *branch,\n     -+\t\t\t\t    const struct string_list *matched_remote_names)\n     ++static void advise_disambiguating_remotes(const char *path, const char *branch,\n     ++\t\t\t\t\t  const struct string_list *matched_remote_names)\n      +{\n      +\tstruct string_list_item *item;\n      +\n     @@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n      +\t\t\t} else if (num_matches > 1) {\n      +\t\t\t\tif (!opts.quiet &&\n      +\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n     -+\t\t\t\t\tadvise_ambiguous_remote(path, branch, &matched_remote_names);\n     ++\t\t\t\t\tadvise_disambiguating_remotes(path, branch,\n     ++\t\t\t\t\t\t\t\t      &matched_remote_names);\n      +\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n      +\t\t\t\t    branch, num_matches);\n       \t\t\t}\n\n-- \ngitgitgadget\n"},{"id":"550943","messageId":"e3f7d885209e6cf9487bea296bc81df43f3758db.1787259838.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v6.git.1787259838.gitgitgadget@gmail.com","subject":"[PATCH v6 1/3] checkout: extract function to display advice for ambiguous remotes","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-20T21:03:56Z","receivedAt":"2026-08-20T21:04:01Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 62 ++++++++++++++++++++++++----------------------\n 1 file changed, 33 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 55e3a89a85..650eda735f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,6 +1343,34 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n+static void advice_disambiguating_remotes(enum checkout_command which_command)\n+{\n+\tconst char *cmdname;\n+\n+\tswitch (which_command) {\n+\tcase CHECKOUT_CHECKOUT:\n+\t\tcmdname = \"checkout\";\n+\t\tbreak;\n+\tcase CHECKOUT_SWITCH:\n+\t\tcmdname = \"switch\";\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n+\t\t    which_command);\n+\t\tbreak;\n+\t}\n+\n+\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t \"checkout.defaultRemote=origin in your config.\"),\n+\t       cmdname);\n+}\n+\n static char *parse_remote_branch(const char *arg,\n \t\t\t\t struct object_id *rev,\n \t\t\t\t int could_be_checkout_paths,\n@@ -1358,35 +1386,11 @@ static char *parse_remote_branch(const char *arg,\n \t}\n \n \tif (!remote && num_matches > 1) {\n-\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n-\t\t    const char *cmdname;\n-\n-\t\t    switch (which_command) {\n-\t\t    case CHECKOUT_CHECKOUT:\n-\t\t\t    cmdname = \"checkout\";\n-\t\t\t    break;\n-\t\t    case CHECKOUT_SWITCH:\n-\t\t\t    cmdname = \"switch\";\n-\t\t\t    break;\n-\t\t    default:\n-\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n-\t\t\t\twhich_command);\n-\t\t\t    break;\n-\t\t    }\n-\n-\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n-\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"    git %s --track origin/<name>\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n-\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n-\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n-\t\t\t   cmdname);\n-\t    }\n-\n-\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n-\t\targ, num_matches);\n+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\tadvice_disambiguating_remotes(which_command);\n+\n+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t    arg, num_matches);\n \t}\n \n \treturn remote;\n-- \ngitgitgadget\n\n"},{"id":"550944","messageId":"97e99ae31e6f0405e1782c96b311f9760de13e42.1787259838.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v6.git.1787259838.gitgitgadget@gmail.com","subject":"[PATCH v6 2/3] checkout: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-20T21:03:57Z","receivedAt":"2026-08-20T21:04:03Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git checkout bar-topic' command that does not\nexactly say which remote they want to work with, and there is no local\nbranch named bar-topic, we try to guess which remote by passing\nbar-topic then create a new branch named bar-topic which tracks the\nremote branch.\n\nIf multiple remotes have a branch named bar-topic, we cannot determine\na single specific remote. Therefore, we provide information that the\nuser can utilize to resolve the issue.\n\nTo make the advice more feasible, we will provide matched remote names\nfor the specified branch name.\n\nTo achieve that, we add an optional feature to the\n`unique_tracking_name()` function that allows the matched remote name\nto be exposed to the caller.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 28 ++++++++++++++++++++--------\n builtin/worktree.c |  4 ++--\n checkout.c         | 14 ++++++++++++--\n checkout.h         |  5 ++++-\n 4 files changed, 38 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 650eda735f..35227f7cbd 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,9 +1343,12 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n-static void advice_disambiguating_remotes(enum checkout_command which_command)\n+static void advise_disambiguating_remotes(enum checkout_command which_command,\n+\t\t\t\t\t  const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n {\n \tconst char *cmdname;\n+\tstruct string_list_item *item;\n \n \tswitch (which_command) {\n \tcase CHECKOUT_CHECKOUT:\n@@ -1360,15 +1363,19 @@ static void advice_disambiguating_remotes(enum checkout_command which_command)\n \t\tbreak;\n \t}\n \n-\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n \t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n \t\t \"\\n\"\n-\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"    git %s --track <remote>/%s\\n\"\n \t\t \"\\n\"\n-\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n \t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n \t\t \"checkout.defaultRemote=origin in your config.\"),\n-\t       cmdname);\n+\t       cmdname, branch);\n }\n \n static char *parse_remote_branch(const char *arg,\n@@ -1377,7 +1384,10 @@ static char *parse_remote_branch(const char *arg,\n \t\t\t\t enum checkout_command which_command)\n {\n \tint num_matches = 0;\n-\tchar *remote = unique_tracking_name(arg, rev, &num_matches);\n+\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\tchar *remote = unique_tracking_name(arg, rev, &num_matches,\n+\t\t\t\t\t    &matched_remote_names);\n \n \tif (remote && could_be_checkout_paths) {\n \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n@@ -1387,12 +1397,14 @@ static char *parse_remote_branch(const char *arg,\n \n \tif (!remote && num_matches > 1) {\n \t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n-\t\t\tadvice_disambiguating_remotes(which_command);\n-\n+\t\t\tadvise_disambiguating_remotes(which_command, arg,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n \t\t    arg, num_matches);\n \t}\n \n+\tstring_list_clear(&matched_remote_names, 0);\n+\n \treturn remote;\n }\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..22c8e5e131 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -782,7 +782,7 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -904,7 +904,7 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 1588b116ee..2806b783ec 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -8,6 +8,7 @@\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n@@ -17,6 +18,7 @@ struct tracking_name_data {\n \tconst char *default_remote;\n \tchar *default_dst_ref;\n \tstruct object_id *default_dst_oid;\n+\tstruct string_list **remote_names;\n };\n \n #define TRACKING_NAME_DATA_INIT { 0 }\n@@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\toidcpy(dst, cb->dst_oid);\n \t\tcb->default_dst_oid = dst;\n \t}\n+\tif (cb->remote_names)\n+\t\tstring_list_append(*cb->remote_names, remote->name);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -48,14 +52,20 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n }\n \n char *unique_tracking_name(const char *name, struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched)\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tconst char *default_remote = NULL;\n-\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n+\n+\tif (!repo_config_get_string_tmp(the_repository,\n+\t\t\t\t\t\"checkout.defaultremote\",\n+\t\t\t\t\t&default_remote))\n \t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n+\tif (dwim_remote_names)\n+\t\tcb_data.remote_names = &dwim_remote_names;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\ndiff --git a/checkout.h b/checkout.h\nindex 55920e7aeb..0b185a0fc9 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -3,6 +3,8 @@\n \n #include \"hash.h\"\n \n+struct string_list;\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n@@ -10,6 +12,7 @@\n  */\n char *unique_tracking_name(const char *name,\n \t\t\t   struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched);\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names);\n \n #endif /* CHECKOUT_H */\n-- \ngitgitgadget\n\n"},{"id":"550945","messageId":"dcb84a69a6a65085d468a0a212cea0281605c5d0.1787259838.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v6.git.1787259838.gitgitgadget@gmail.com","subject":"[PATCH v6 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-20T21:03:58Z","receivedAt":"2026-08-20T21:04:04Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add ../foo-dir bar-topic' command\nthat does not exactly say which remote they want to work with, and\nthere is no local branch named bar-topic, we try to guess which remote\nby passing bar-topic then create a new branch named bar-topic which\ntracks the remote branch.\n\nIf there are multiple remotes that have branch named bar-topic, we\nsilently gave up, leaving the variable 'branch' intact.  Then we\nentered the conditional clause 'if (!opts.orphan &&\n!lookup_commit_reference_by_name(branch))' and triggered \"invalid\nreference\" error.  This error message did not contain enough\ninformation to resolve the issue where the remote could not be\nguessed.\n\nTo improve the situation, we display a hint and a descriptive error\nmessage and die immediately when multiple matching branches are found.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/worktree.c      | 35 +++++++++++++++++++++++++++++++++--\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 35 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 22c8e5e131..8286c283e0 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -788,6 +788,25 @@ static char *dwim_branch(const char *path, char **new_branch)\n \treturn NULL;\n }\n \n+static void advise_disambiguating_remotes(const char *path, const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n+{\n+\tstruct string_list_item *item;\n+\n+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n+\t\t \"<remote>, you can do so by:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n+\t       branch, path, branch);\n+}\n+\n static int add(int ac, const char **av, const char *prefix,\n \t       struct repository *repo UNUSED)\n {\n@@ -900,15 +919,27 @@ static int add(int ac, const char **av, const char *prefix,\n \t} else if (ac == 2) {\n \t\tstruct object_id oid;\n \t\tstruct commit *commit;\n-\t\tchar *remote;\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n+\t\t\tchar *remote;\n+\t\t\tint num_matches = 0;\n+\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet &&\n+\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\t\tadvise_disambiguating_remotes(path, branch,\n+\t\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n+\t\t\tstring_list_clear(&matched_remote_names, 0);\n \t\t}\n \n \t\tif (!strcmp(branch, \"HEAD\"))\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n-- \ngitgitgadget\n"},{"id":"550976","messageId":"xmqqa4qgruvj.fsf@gitster.g","threadId":"66141","inReplyTo":"dcb84a69a6a65085d468a0a212cea0281605c5d0.1787259838.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v6 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-21T03:54:24Z","receivedAt":"2026-08-21T03:54:27Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n>\n> When the user runs 'git worktree add ../foo-dir bar-topic' command\n> that does not exactly say which remote they want to work with, and\n> there is no local branch named bar-topic, we try to guess which remote\n> by passing bar-topic then create a new branch named bar-topic which\n> tracks the remote branch.\n>\n> If there are multiple remotes that have branch named bar-topic, we\n> silently gave up, leaving the variable 'branch' intact.  Then we\n> entered the conditional clause 'if (!opts.orphan &&\n> !lookup_commit_reference_by_name(branch))' and triggered \"invalid\n> reference\" error.  This error message did not contain enough\n> information to resolve the issue where the remote could not be\n> guessed.\n>\n> To improve the situation, we display a hint and a descriptive error\n> message and die immediately when multiple matching branches are found.\n>\n> Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n> ---\n>  builtin/worktree.c      | 35 +++++++++++++++++++++++++++++++++--\n>  t/t2400-worktree-add.sh |  4 ++--\n>  2 files changed, 35 insertions(+), 4 deletions(-)\n>\n> diff --git a/builtin/worktree.c b/builtin/worktree.c\n> index 22c8e5e131..8286c283e0 100644\n> --- a/builtin/worktree.c\n> +++ b/builtin/worktree.c\n> @@ -788,6 +788,25 @@ static char *dwim_branch(const char *path, char **new_branch)\n>  \treturn NULL;\n>  }\n>  \n> +static void advise_disambiguating_remotes(const char *path, const char *branch,\n> +\t\t\t\t\t  const struct string_list *matched_remote_names)\n> +{\n> +\tstruct string_list_item *item;\n> +\n> +\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n\nThe subject \"Branches\" calls for plural verb \"appear\" (not\n\"appears\").  The same issue appears in [PATCH 2/3].\n\n>  \t\tif (!commit) {\n> -\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n> +\t\t\tchar *remote;\n> +\t\t\tint num_matches = 0;\n> +\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n> +\n> +\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n> +\t\t\t\t\t\t      &matched_remote_names);\n>  \t\t\tif (remote) {\n>  \t\t\t\tnew_branch = branch;\n>  \t\t\t\tbranch = new_branch_to_free = remote;\n> +\t\t\t} else if (num_matches > 1) {\n> +\t\t\t\tif (!opts.quiet &&\n> +\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> +\t\t\t\t\tadvise_disambiguating_remotes(path, branch,\n> +\t\t\t\t\t\t\t\t      &matched_remote_names);\n> +\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n> +\t\t\t\t    branch, num_matches);\n>  \t\t\t}\n> +\t\t\tstring_list_clear(&matched_remote_names, 0);\n>  \t\t}\n\nThis appears inside \"} else if (ac == 2) {\" to catch an invocation\nlike\n\n\tgit worktree add ../over-there topic-branch\n\nwhere the origin of topic-branch is ambiguous (in other words,\nappears in multiple remotes).  But don't we have the same issue for\n1 argument case that appears just above this (ac == 2) case that\nhandles\n\n\tgit worktree add ../topic-branch\n\ninvocation?  The code reads like:\n\n\t} else if (ac < 2) {\n\t\t/* DWIM: Guess branch name from path. */\n\t\tchar *s = dwim_branch(path, &new_branch_to_free);\n\t\tif (s)\n\t\t\tbranch = branch_to_free = s;\n\t\tnew_branch = new_branch_to_free;\n\n\t\t/* DWIM: Infer --orphan when repo has no refs. */\n\t\topts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n\t} else if (ac == 2) {\n\nwhere the branch name \"topic-branch\" is guessed from the path by\ncalling dwim_branch(), and we would get NULL in s.  branch is left\nas-is, so it becomes \"HEAD\" that was assigned much earlier in the\nsame function.\n\n        branch = ac < 2 ? \"HEAD\" : av[1];\n\nWe would create a new directory in ../topic-branch next door, and\nthen which branch would we check out?  Would dwim_orphan() kick in?\n\nPerhaps we want to update that code path to disambiguate the same way?\n\n> diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\n> index 87b926728a..5c105cf252 100755\n> --- a/t/t2400-worktree-add.sh\n> +++ b/t/t2400-worktree-add.sh\n> @@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n>  test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n>  \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n>  \tsetup_remote_repo repo_upstream repo_dwim &&\n> -\tgit init repo_dwim &&\n>  \t(\n>  \t\tcd repo_dwim &&\n>  \t\tgit remote add repo_upstream2 ../repo_upstream &&\n>  \t\tgit fetch repo_upstream2 &&\n> -\t\ttest_must_fail git worktree add ../foo foo &&\n> +\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n> +\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n>  \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n>  \t\tgit status -uno --porcelain >status.actual &&\n>  \t\ttest_must_be_empty status.actual\n"},{"id":"551044","messageId":"CAF5D8-vDzp9xhu96Tu0ScmWRHvVyi74MD0NhPMnQ9ayoy6h+wQ@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqa4qgruvj.fsf@gitster.g","subject":"Re: [PATCH v6 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-21T23:15:49Z","receivedAt":"2026-08-21T23:16:01Z","isPatch":true,"body":"On Fri, Aug 21, 2026 at 12:54 PM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> \"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n> > From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n> >\n> > diff --git a/builtin/worktree.c b/builtin/worktree.c\n> > index 22c8e5e131..8286c283e0 100644\n> > --- a/builtin/worktree.c\n> > +++ b/builtin/worktree.c\n> > @@ -788,6 +788,25 @@ static char *dwim_branch(const char *path, char **new_branch)\n> >       return NULL;\n> >  }\n> >\n> > +static void advise_disambiguating_remotes(const char *path, const char *branch,\n> > +                                       const struct string_list *matched_remote_names)\n> > +{\n> > +     struct string_list_item *item;\n> > +\n> > +     advise(_(\"Branches with the same name appears in multiple remotes:\"));\n>\n> The subject \"Branches\" calls for plural verb \"appear\" (not\n> \"appears\").  The same issue appears in [PATCH 2/3].\n\nI overlooked that. Thank you.\nRather than simply matching the verb to the subject, I want to clarify\nwhat (as specified by the user) exists on multiple remotes:\n    advise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n\n> >               if (!commit) {\n> > -                     remote = unique_tracking_name(branch, &oid, NULL, NULL);\n> > +                     char *remote;\n> > +                     int num_matches = 0;\n> > +                     struct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n> > +\n> > +                     remote = unique_tracking_name(branch, &oid, &num_matches,\n> > +                                                   &matched_remote_names);\n> >                       if (remote) {\n> >                               new_branch = branch;\n> >                               branch = new_branch_to_free = remote;\n> > +                     } else if (num_matches > 1) {\n> > +                             if (!opts.quiet &&\n> > +                                 advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> > +                                     advise_disambiguating_remotes(path, branch,\n> > +                                                                   &matched_remote_names);\n> > +                             die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n> > +                                 branch, num_matches);\n> >                       }\n> > +                     string_list_clear(&matched_remote_names, 0);\n> >               }\n>\n> This appears inside \"} else if (ac == 2) {\" to catch an invocation\n> like\n>\n>         git worktree add ../over-there topic-branch\n>\n> where the origin of topic-branch is ambiguous (in other words,\n> appears in multiple remotes).  But don't we have the same issue for\n> 1 argument case that appears just above this (ac == 2) case that\n> handles\n>\n>         git worktree add ../topic-branch\n>\n> invocation?  The code reads like:\n>\n>         } else if (ac < 2) {\n>                 /* DWIM: Guess branch name from path. */\n>                 char *s = dwim_branch(path, &new_branch_to_free);\n>                 if (s)\n>                         branch = branch_to_free = s;\n>                 new_branch = new_branch_to_free;\n>\n>                 /* DWIM: Infer --orphan when repo has no refs. */\n>                 opts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n>         } else if (ac == 2) {\n>\n> where the branch name \"topic-branch\" is guessed from the path by\n> calling dwim_branch(), and we would get NULL in s.  branch is left\n> as-is, so it becomes \"HEAD\" that was assigned much earlier in the\n> same function.\n>\n>         branch = ac < 2 ? \"HEAD\" : av[1];\n>\n> We would create a new directory in ../topic-branch next door, and\n> then which branch would we check out?  Would dwim_orphan() kick in?\n>\n> Perhaps we want to update that code path to disambiguate the same way?\n\nIn the case of\n         git worktree add ../topic-branch\ninvocation, multiple match can occur in dwim_branch() if there is a\n'worktree.guessremote=true' config or one specifies '--guess-remote'\noption.Then it creates a branch named 'topic-branch' from HEAD, and\nthe command exits with success.\n\nMy initial patch included a warning and advice here,\nbut now I don't think they are necessary.\n\nEven if multiple remotes match here, the command completes\nsuccessfully. This could well be the intended behavior\n(just as when there is no match). In that case, a warning\nor advice might be superfluous.\n\nFrom the perspective of offering advice that actually\nhelps the user, since the branch and worktree have already\nbeen created, the appropriate guidance would be to suggest\ndeleting them and starting over. That, however, would\nlikely make the message even longer.\n\nIf there were an option (which currently doesn't exist)\nto make the command fail when remote inference fails,\nthen I think it would be appropriate to issue the same\nadvice and error message as in \"ac == 2\" case.\n\nThanks,\n--\nYoichi NAKAYAMA\n"},{"id":"551045","messageId":"xmqqzeyfxcdu.fsf@gitster.g","threadId":"66141","inReplyTo":"CAF5D8-vDzp9xhu96Tu0ScmWRHvVyi74MD0NhPMnQ9ayoy6h+wQ@mail.gmail.com","subject":"Re: [PATCH v6 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-21T23:49:33Z","receivedAt":"2026-08-21T23:49:36Z","isPatch":true,"body":"Yoichi Nakayama <yoichi.nakayama@gmail.com> writes:\n\n> My initial patch included a warning and advice here,\n> but now I don't think they are necessary.\n>\n> Even if multiple remotes match here, the command completes\n> successfully. This could well be the intended behavior\n> (just as when there is no match). In that case, a warning\n> or advice might be superfluous.\n\nIn other words, there is no point in calling dwim_branch() from that\ncode path, as the end result is exactly the same whether no remotes\nmatch, exactly one remote matches, or two or more remotes match?\nWould it then make sense to leave a note there to consider later if\nthe dwim_branch() call can be removed?\n\nIt is a bit hard to believe that is the intended behavior, but OK.\nIt does not regress the current behavior in any way.\n\nThanks.\n\n\n"},{"id":"551046","messageId":"CAF5D8-trxeMd8HYzy6kD4myf+bynkxOvxKDQrMdmqnvcdpdkEA@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqzeyfxcdu.fsf@gitster.g","subject":"Re: [PATCH v6 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-22T00:50:48Z","receivedAt":"2026-08-22T00:51:00Z","isPatch":true,"body":"On Sat, Aug 22, 2026 at 8:49 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Yoichi Nakayama <yoichi.nakayama@gmail.com> writes:\n>\n> > My initial patch included a warning and advice here,\n> > but now I don't think they are necessary.\n> >\n> > Even if multiple remotes match here, the command completes\n> > successfully. This could well be the intended behavior\n> > (just as when there is no match). In that case, a warning\n> > or advice might be superfluous.\n>\n> In other words, there is no point in calling dwim_branch() from that\n> code path, as the end result is exactly the same whether no remotes\n> match, exactly one remote matches, or two or more remotes match?\n> Would it then make sense to leave a note there to consider later if\n> the dwim_branch() call can be removed?\n\nNo. The exit codes of the command 'git worktree add ../topic-branch'\nare the same (== 0). but the results are different.\n\nIf there is a unique match found in dwim_branch(), it creates a local\nbranch named topic-branch which tracks <remote>/topic-branch.\nIn case of no match or multiple matches, it creates a local branch\nnamed topic-branch from HEAD.\n\nSince Git treats both cases as successful, either can be considered\nthe intended behavior.\n(Although, if there are multiple matches, there is a fair chance the\nresult might not be what was intended.)\n\nI am confident that it is appropriate to provide a hint when a command\nfails, but it is difficult to decide what to do when a command succeeds.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"551047","messageId":"pull.2197.v7.git.1787368962.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v7 0/3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-22T03:22:39Z","receivedAt":"2026-08-22T03:22:45Z","isPatch":true,"body":"'git worktree add ../foo-dir bar-topic' fails to dwim when there are\nmultiple remote branches with name `bar-topic'. But it doesn't display\nmeaningful message as 'git checkout bar-topic' does under the same\nsituation.\n\nWe improve this by adding advice and modify the error message for worktree\nadd.\n\nBy Junio's suggestion, we include matched remote names in the advice. It is\napplied to checkout, too.\n\nChanges from the previous patch:\n\n * fix grammatical errors in hints\n * narrow the scope of local variable oid\n\nYoichi NAKAYAMA (3):\n  checkout: extract function to display advice for ambiguous remotes\n  checkout: improve message for ambiguous remote branch name\n  worktree add: improve message for ambiguous remote branch name\n\n builtin/checkout.c      | 76 +++++++++++++++++++++++++----------------\n builtin/worktree.c      | 39 ++++++++++++++++++---\n checkout.c              | 14 ++++++--\n checkout.h              |  5 ++-\n t/t2400-worktree-add.sh |  4 +--\n 5 files changed, 99 insertions(+), 39 deletions(-)\n\n\nbase-commit: dea0ea3582e6980ddbc1173cc8e3e9f9db91cde0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v7\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v7\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v6:\n\n 1:  e3f7d88520 = 1:  e3f7d88520 checkout: extract function to display advice for ambiguous remotes\n 2:  97e99ae31e ! 2:  89c0f4d303 checkout: improve message for ambiguous remote branch name\n     @@ Metadata\n       ## Commit message ##\n          checkout: improve message for ambiguous remote branch name\n      \n     -    When the user runs 'git checkout bar-topic' command that does not\n     -    exactly say which remote they want to work with, and there is no local\n     -    branch named bar-topic, we try to guess which remote by passing\n     -    bar-topic then create a new branch named bar-topic which tracks the\n     -    remote branch.\n     +    When the user runs 'git checkout bar-topic' without specifying a\n     +    remote, and there is no local branch named bar-topic, we try to guess\n     +    which remote branch bar-topic refers to, then create a new branch\n     +    named bar-topic that tracks the remote branch.\n      \n          If multiple remotes have a branch named bar-topic, we cannot determine\n     -    a single specific remote. Therefore, we provide information that the\n     -    user can utilize to resolve the issue.\n     +    a single remote.\n      \n     -    To make the advice more feasible, we will provide matched remote names\n     -    for the specified branch name.\n     +    To make it easier to resolve the ambiguity, provide the names of the\n     +    matching remotes for the specified branch name.\n      \n     -    To achieve that, we add an optional feature to the\n     -    `unique_tracking_name()` function that allows the matched remote name\n     -    to be exposed to the caller.\n     +    To achieve that, add an optional feature to the\n     +    `unique_tracking_name()` function that allows the matching remote\n     +    names to be exposed to the caller.\n      \n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n     @@ builtin/checkout.c: static void advice_disambiguating_remotes(enum checkout_comm\n       \t}\n       \n      -\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n     -+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n     ++\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n      +\tfor_each_string_list_item(item, matched_remote_names) {\n      +\t\tadvise(_(\"  %s\"), item->string);\n      +\t}\n 3:  dcb84a69a6 ! 3:  095a5796d4 worktree add: improve message for ambiguous remote branch name\n     @@ Metadata\n       ## Commit message ##\n          worktree add: improve message for ambiguous remote branch name\n      \n     -    When the user runs 'git worktree add ../foo-dir bar-topic' command\n     -    that does not exactly say which remote they want to work with, and\n     -    there is no local branch named bar-topic, we try to guess which remote\n     -    by passing bar-topic then create a new branch named bar-topic which\n     -    tracks the remote branch.\n     +    When the user runs 'git worktree add ../foo-dir bar-topic' without\n     +    specifying a remote, and there is no local branch named bar-topic, we\n     +    try to guess which remote branch bar-topic refers to, then create a\n     +    new branch named bar-topic that tracks the remote branch.\n      \n     -    If there are multiple remotes that have branch named bar-topic, we\n     -    silently gave up, leaving the variable 'branch' intact.  Then we\n     -    entered the conditional clause 'if (!opts.orphan &&\n     -    !lookup_commit_reference_by_name(branch))' and triggered \"invalid\n     -    reference\" error.  This error message did not contain enough\n     -    information to resolve the issue where the remote could not be\n     -    guessed.\n     +    If multiple remotes have a branch named bar-topic, we silently gave\n     +    up, leaving the variable 'branch' intact.  We then entered the\n     +    conditional clause 'if (!opts.orphan &&\n     +    !lookup_commit_reference_by_name(branch))' and triggered an \"invalid\n     +    reference\" error.  This error message did not provide enough\n     +    information to resolve the ambiguity.\n      \n     -    To improve the situation, we display a hint and a descriptive error\n     -    message and die immediately when multiple matching branches are found.\n     +    When multiple matching branches are found, display a hint and a\n     +    descriptive error message and die immediately.\n      \n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n     @@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch\n      +{\n      +\tstruct string_list_item *item;\n      +\n     -+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n     ++\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n      +\tfor_each_string_list_item(item, matched_remote_names) {\n      +\t\tadvise(_(\"  %s\"), item->string);\n      +\t}\n     @@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch\n       \t       struct repository *repo UNUSED)\n       {\n      @@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n     + \t\t/* DWIM: Infer --orphan when repo has no refs. */\n     + \t\topts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n       \t} else if (ac == 2) {\n     - \t\tstruct object_id oid;\n     +-\t\tstruct object_id oid;\n       \t\tstruct commit *commit;\n      -\t\tchar *remote;\n       \n       \t\tcommit = lookup_commit_reference_by_name(branch);\n       \t\tif (!commit) {\n      -\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n     ++\t\t\tstruct object_id oid;\n      +\t\t\tchar *remote;\n      +\t\t\tint num_matches = 0;\n      +\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n\n-- \ngitgitgadget\n"},{"id":"551048","messageId":"e3f7d885209e6cf9487bea296bc81df43f3758db.1787368962.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v7.git.1787368962.gitgitgadget@gmail.com","subject":"[PATCH v7 1/3] checkout: extract function to display advice for ambiguous remotes","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-22T03:22:40Z","receivedAt":"2026-08-22T03:22:47Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 62 ++++++++++++++++++++++++----------------------\n 1 file changed, 33 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 55e3a89a85..650eda735f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,6 +1343,34 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n+static void advice_disambiguating_remotes(enum checkout_command which_command)\n+{\n+\tconst char *cmdname;\n+\n+\tswitch (which_command) {\n+\tcase CHECKOUT_CHECKOUT:\n+\t\tcmdname = \"checkout\";\n+\t\tbreak;\n+\tcase CHECKOUT_SWITCH:\n+\t\tcmdname = \"switch\";\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n+\t\t    which_command);\n+\t\tbreak;\n+\t}\n+\n+\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t \"checkout.defaultRemote=origin in your config.\"),\n+\t       cmdname);\n+}\n+\n static char *parse_remote_branch(const char *arg,\n \t\t\t\t struct object_id *rev,\n \t\t\t\t int could_be_checkout_paths,\n@@ -1358,35 +1386,11 @@ static char *parse_remote_branch(const char *arg,\n \t}\n \n \tif (!remote && num_matches > 1) {\n-\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n-\t\t    const char *cmdname;\n-\n-\t\t    switch (which_command) {\n-\t\t    case CHECKOUT_CHECKOUT:\n-\t\t\t    cmdname = \"checkout\";\n-\t\t\t    break;\n-\t\t    case CHECKOUT_SWITCH:\n-\t\t\t    cmdname = \"switch\";\n-\t\t\t    break;\n-\t\t    default:\n-\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n-\t\t\t\twhich_command);\n-\t\t\t    break;\n-\t\t    }\n-\n-\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n-\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"    git %s --track origin/<name>\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n-\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n-\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n-\t\t\t   cmdname);\n-\t    }\n-\n-\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n-\t\targ, num_matches);\n+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\tadvice_disambiguating_remotes(which_command);\n+\n+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t    arg, num_matches);\n \t}\n \n \treturn remote;\n-- \ngitgitgadget\n\n"},{"id":"551049","messageId":"89c0f4d30317ebf7cda884710944e9a6f23d46fe.1787368962.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v7.git.1787368962.gitgitgadget@gmail.com","subject":"[PATCH v7 2/3] checkout: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-22T03:22:41Z","receivedAt":"2026-08-22T03:22:49Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git checkout bar-topic' without specifying a\nremote, and there is no local branch named bar-topic, we try to guess\nwhich remote branch bar-topic refers to, then create a new branch\nnamed bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we cannot determine\na single remote.\n\nTo make it easier to resolve the ambiguity, provide the names of the\nmatching remotes for the specified branch name.\n\nTo achieve that, add an optional feature to the\n`unique_tracking_name()` function that allows the matching remote\nnames to be exposed to the caller.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 28 ++++++++++++++++++++--------\n builtin/worktree.c |  4 ++--\n checkout.c         | 14 ++++++++++++--\n checkout.h         |  5 ++++-\n 4 files changed, 38 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 650eda735f..07373615d8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,9 +1343,12 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n-static void advice_disambiguating_remotes(enum checkout_command which_command)\n+static void advise_disambiguating_remotes(enum checkout_command which_command,\n+\t\t\t\t\t  const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n {\n \tconst char *cmdname;\n+\tstruct string_list_item *item;\n \n \tswitch (which_command) {\n \tcase CHECKOUT_CHECKOUT:\n@@ -1360,15 +1363,19 @@ static void advice_disambiguating_remotes(enum checkout_command which_command)\n \t\tbreak;\n \t}\n \n-\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n \t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n \t\t \"\\n\"\n-\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"    git %s --track <remote>/%s\\n\"\n \t\t \"\\n\"\n-\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n \t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n \t\t \"checkout.defaultRemote=origin in your config.\"),\n-\t       cmdname);\n+\t       cmdname, branch);\n }\n \n static char *parse_remote_branch(const char *arg,\n@@ -1377,7 +1384,10 @@ static char *parse_remote_branch(const char *arg,\n \t\t\t\t enum checkout_command which_command)\n {\n \tint num_matches = 0;\n-\tchar *remote = unique_tracking_name(arg, rev, &num_matches);\n+\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\tchar *remote = unique_tracking_name(arg, rev, &num_matches,\n+\t\t\t\t\t    &matched_remote_names);\n \n \tif (remote && could_be_checkout_paths) {\n \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n@@ -1387,12 +1397,14 @@ static char *parse_remote_branch(const char *arg,\n \n \tif (!remote && num_matches > 1) {\n \t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n-\t\t\tadvice_disambiguating_remotes(which_command);\n-\n+\t\t\tadvise_disambiguating_remotes(which_command, arg,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n \t\t    arg, num_matches);\n \t}\n \n+\tstring_list_clear(&matched_remote_names, 0);\n+\n \treturn remote;\n }\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..22c8e5e131 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -782,7 +782,7 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -904,7 +904,7 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 1588b116ee..2806b783ec 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -8,6 +8,7 @@\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n@@ -17,6 +18,7 @@ struct tracking_name_data {\n \tconst char *default_remote;\n \tchar *default_dst_ref;\n \tstruct object_id *default_dst_oid;\n+\tstruct string_list **remote_names;\n };\n \n #define TRACKING_NAME_DATA_INIT { 0 }\n@@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\toidcpy(dst, cb->dst_oid);\n \t\tcb->default_dst_oid = dst;\n \t}\n+\tif (cb->remote_names)\n+\t\tstring_list_append(*cb->remote_names, remote->name);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -48,14 +52,20 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n }\n \n char *unique_tracking_name(const char *name, struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched)\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tconst char *default_remote = NULL;\n-\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n+\n+\tif (!repo_config_get_string_tmp(the_repository,\n+\t\t\t\t\t\"checkout.defaultremote\",\n+\t\t\t\t\t&default_remote))\n \t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n+\tif (dwim_remote_names)\n+\t\tcb_data.remote_names = &dwim_remote_names;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\ndiff --git a/checkout.h b/checkout.h\nindex 55920e7aeb..0b185a0fc9 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -3,6 +3,8 @@\n \n #include \"hash.h\"\n \n+struct string_list;\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n@@ -10,6 +12,7 @@\n  */\n char *unique_tracking_name(const char *name,\n \t\t\t   struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched);\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names);\n \n #endif /* CHECKOUT_H */\n-- \ngitgitgadget\n\n"},{"id":"551050","messageId":"095a5796d4c92012825daa07eef67140a7461018.1787368962.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v7.git.1787368962.gitgitgadget@gmail.com","subject":"[PATCH v7 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-22T03:22:42Z","receivedAt":"2026-08-22T03:22:51Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add ../foo-dir bar-topic' without\nspecifying a remote, and there is no local branch named bar-topic, we\ntry to guess which remote branch bar-topic refers to, then create a\nnew branch named bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we silently gave\nup, leaving the variable 'branch' intact.  We then entered the\nconditional clause 'if (!opts.orphan &&\n!lookup_commit_reference_by_name(branch))' and triggered an \"invalid\nreference\" error.  This error message did not provide enough\ninformation to resolve the ambiguity.\n\nWhen multiple matching branches are found, display a hint and a\ndescriptive error message and die immediately.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/worktree.c      | 37 ++++++++++++++++++++++++++++++++++---\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 36 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 22c8e5e131..320b18873d 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -788,6 +788,25 @@ static char *dwim_branch(const char *path, char **new_branch)\n \treturn NULL;\n }\n \n+static void advise_disambiguating_remotes(const char *path, const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n+{\n+\tstruct string_list_item *item;\n+\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n+\t\t \"<remote>, you can do so by:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n+\t       branch, path, branch);\n+}\n+\n static int add(int ac, const char **av, const char *prefix,\n \t       struct repository *repo UNUSED)\n {\n@@ -898,17 +917,29 @@ static int add(int ac, const char **av, const char *prefix,\n \t\t/* DWIM: Infer --orphan when repo has no refs. */\n \t\topts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n \t} else if (ac == 2) {\n-\t\tstruct object_id oid;\n \t\tstruct commit *commit;\n-\t\tchar *remote;\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n+\t\t\tstruct object_id oid;\n+\t\t\tchar *remote;\n+\t\t\tint num_matches = 0;\n+\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet &&\n+\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\t\tadvise_disambiguating_remotes(path, branch,\n+\t\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n+\t\t\tstring_list_clear(&matched_remote_names, 0);\n \t\t}\n \n \t\tif (!strcmp(branch, \"HEAD\"))\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n-- \ngitgitgadget\n"},{"id":"551054","messageId":"xmqqld9yvznl.fsf@gitster.g","threadId":"66141","inReplyTo":"CAF5D8-trxeMd8HYzy6kD4myf+bynkxOvxKDQrMdmqnvcdpdkEA@mail.gmail.com","subject":"Re: [PATCH v6 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-22T17:22:06Z","receivedAt":"2026-08-22T17:22:09Z","isPatch":true,"body":"Yoichi Nakayama <yoichi.nakayama@gmail.com> writes:\n\n> No. The exit codes of the command 'git worktree add ../topic-branch'\n> are the same (== 0). but the results are different.\n>\n> If there is a unique match found in dwim_branch(), it creates a local\n> branch named topic-branch which tracks <remote>/topic-branch.\n> In case of no match or multiple matches, it creates a local branch\n> named topic-branch from HEAD.\n>\n> Since Git treats both cases as successful, either can be considered\n> the intended behavior.\n> (Although, if there are multiple matches, there is a fair chance the\n> result might not be what was intended.)\n>\n> I am confident that it is appropriate to provide a hint when a command\n> fails, but it is difficult to decide what to do when a command succeeds.\n\nI actually think it falls into the same class of bug you are fixing\nin this topic, which was caused by not considering the possibility\nthat there can be any case other than 0-match and 1-match, and not\nthinking through the ramifications of treating 2-match and 0-match\nthe same way.\n\nIt is of course OK to fix one bug and leave the other one\nunaddressed, to be fixed in a later follow-up effort.\n\nThe rest of this message is only for those who will tackle the\n\"later follow-up effort\" part after the dust settles once the\ncurrent topic lands (aka #leftoverbits).\n\nIn the beginning, before Thomas Gummerer started his topic in\nNovember 2017 [*1*], 'git worktree add <path> [<branch>]' created a\nnew branch from the checked-out HEAD, without looking at any\nremote.\n\n - 'git worktree add <path> <branch>' before Thomas's effort errored\n   out if <branch> did not exist.  It was safe to add DWIM from\n   remote-tracking branches without requiring any option.\n\n - 'git worktree add <path>' used to create a new branch whose name\n   is derived from basename(path) that points at the current HEAD,\n   without erroring out.  Enabling DWIM from remote-tracking\n   branches unconditionally would have meant a silent behavior\n   change.  So DWIM was added to this case to require the\n   '--guess-remote' option to enable [*2*].\n\nBack then, unique_tracking_name() did not let the callers\ndistinguish between 0-match and multiple-match cases, so when you\nhad multiple matches, 'git worktree add <path> [<branch>]' triggered\nthe same code path as 0-matches.  When the DWIM feature was\ndesigned, handling the multiple-match case correctly was on nobody's\nradar.\n\nEven when Ævar Arnfjörð Bjarmason updated unique_tracking_name() in\n3c87aa946a (checkout: pass the \"num_matches\" up to callers,\n2018-06-05), in a topic that ends at 8d7b558bae (checkout &\nworktree: introduce checkout.defaultRemote, 2018-06-05), to allow\ncallers to distinguish between 0-match and ambiguous multi-match\ncases, this work unfortunately concentrated on improving \"git\ncheckout\", and callers of unique_tracking_name() in \"git worktree\"\nwere updated to pass NULL, i.e., teaching them to count how many\nmatches they got was postponed.\n\nWe know that the update to unique_tracking_name() in this work back\nthen was not complete on the \"git worktree\" side.  After all, that\nis how this topic arose to fix one of the two code paths that call\nthe function so that we react differently between 0-match and\nmultiple-match cases.\n\nNow that we are aware of the issue, I think the code should error\nout, instead of creating the new branch out of HEAD, when there are\nmultiple remotes with the name of the branch.  In other words, the\nexisting code that behaves the same way in 0-match and 2-match cases\nis buggy, and we should eventually fix it.\n\n\n[Footnotes]\n\n *1* https://lore.kernel.org/git/20171112134305.3949-1-t.gummerer@gmail.com/\n *2* https://lore.kernel.org/git/20171126194356.16187-1-t.gummerer@gmail.com/\n"},{"id":"551056","messageId":"xmqqv792uixb.fsf@gitster.g","threadId":"66141","inReplyTo":"pull.2197.v7.git.1787368962.gitgitgadget@gmail.com","subject":"Re: [PATCH v7 0/3] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-22T18:08:48Z","receivedAt":"2026-08-22T18:08:50Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n>  * fix grammatical errors in hints\n>  * narrow the scope of local variable oid\n\nBoth changes look sensible.  I very much like the new advise()\nmessage that is much more concise.\n\n>      -+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n>      ++\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n>      -+\tadvise(_(\"Branches with the same name appears in multiple remotes:\"));\n>      ++\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n\nWill queue.\n"},{"id":"551158","messageId":"CAF5D8-uDROnfs9gZXm1gwQdsOr-xP5Ai3OG-w7eOSHos3_pa8Q@mail.gmail.com","threadId":"66141","inReplyTo":"xmqqld9yvznl.fsf@gitster.g","subject":"Re: [PATCH v6 3/3] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi Nakayama","fromEmail":"yoichi.nakayama@gmail.com","sentAt":"2026-08-24T22:25:57Z","receivedAt":"2026-08-24T22:26:10Z","isPatch":true,"body":"On Sun, Aug 23, 2026 at 2:22 AM Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Yoichi Nakayama <yoichi.nakayama@gmail.com> writes:\n>\n> > No. The exit codes of the command 'git worktree add ../topic-branch'\n> > are the same (== 0). but the results are different.\n> >\n> > If there is a unique match found in dwim_branch(), it creates a local\n> > branch named topic-branch which tracks <remote>/topic-branch.\n> > In case of no match or multiple matches, it creates a local branch\n> > named topic-branch from HEAD.\n> >\n> > Since Git treats both cases as successful, either can be considered\n> > the intended behavior.\n> > (Although, if there are multiple matches, there is a fair chance the\n> > result might not be what was intended.)\n> >\n> > I am confident that it is appropriate to provide a hint when a command\n> > fails, but it is difficult to decide what to do when a command succeeds.\n>\n> I actually think it falls into the same class of bug you are fixing\n> in this topic, which was caused by not considering the possibility\n> that there can be any case other than 0-match and 1-match, and not\n> thinking through the ramifications of treating 2-match and 0-match\n> the same way.\n>\n> It is of course OK to fix one bug and leave the other one\n> unaddressed, to be fixed in a later follow-up effort.\n>\n> The rest of this message is only for those who will tackle the\n> \"later follow-up effort\" part after the dust settles once the\n> current topic lands (aka #leftoverbits).\n>\n> In the beginning, before Thomas Gummerer started his topic in\n> November 2017 [*1*], 'git worktree add <path> [<branch>]' created a\n> new branch from the checked-out HEAD, without looking at any\n> remote.\n>\n>  - 'git worktree add <path> <branch>' before Thomas's effort errored\n>    out if <branch> did not exist.  It was safe to add DWIM from\n>    remote-tracking branches without requiring any option.\n>\n>  - 'git worktree add <path>' used to create a new branch whose name\n>    is derived from basename(path) that points at the current HEAD,\n>    without erroring out.  Enabling DWIM from remote-tracking\n>    branches unconditionally would have meant a silent behavior\n>    change.  So DWIM was added to this case to require the\n>    '--guess-remote' option to enable [*2*].\n>\n> Back then, unique_tracking_name() did not let the callers\n> distinguish between 0-match and multiple-match cases, so when you\n> had multiple matches, 'git worktree add <path> [<branch>]' triggered\n> the same code path as 0-matches.  When the DWIM feature was\n> designed, handling the multiple-match case correctly was on nobody's\n> radar.\n>\n> Even when Ævar Arnfjörð Bjarmason updated unique_tracking_name() in\n> 3c87aa946a (checkout: pass the \"num_matches\" up to callers,\n> 2018-06-05), in a topic that ends at 8d7b558bae (checkout &\n> worktree: introduce checkout.defaultRemote, 2018-06-05), to allow\n> callers to distinguish between 0-match and ambiguous multi-match\n> cases, this work unfortunately concentrated on improving \"git\n> checkout\", and callers of unique_tracking_name() in \"git worktree\"\n> were updated to pass NULL, i.e., teaching them to count how many\n> matches they got was postponed.\n>\n> We know that the update to unique_tracking_name() in this work back\n> then was not complete on the \"git worktree\" side.  After all, that\n> is how this topic arose to fix one of the two code paths that call\n> the function so that we react differently between 0-match and\n> multiple-match cases.\n>\n> Now that we are aware of the issue, I think the code should error\n> out, instead of creating the new branch out of HEAD, when there are\n> multiple remotes with the name of the branch.  In other words, the\n> existing code that behaves the same way in 0-match and 2-match cases\n> is buggy, and we should eventually fix it.\n>\n>\n> [Footnotes]\n>\n>  *1* https://lore.kernel.org/git/20171112134305.3949-1-t.gummerer@gmail.com/\n>  *2* https://lore.kernel.org/git/20171126194356.16187-1-t.gummerer@gmail.com/\n\nThank you for the analysis. I believe the behavior of treating multiple matches\nas an error is appropriate.\n\nI feel that now is the time to implement the fix that had been postponed.\nI'll make another commit for it.\n\nThanks,\n-- \nYoichi NAKAYAMA\n"},{"id":"551239","messageId":"pull.2197.v8.git.1787691875.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v8 0/4] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-25T21:04:31Z","receivedAt":"2026-08-25T21:04:39Z","isPatch":true,"body":"'git worktree add ../foo-dir bar-topic' fails to dwim when there are\nmultiple remote branches with name `bar-topic'. But it doesn't display\nmeaningful message as 'git checkout bar-topic' does under the same\nsituation.\n\nWe improve this by adding advice and modify the error message for worktree\nadd.\n\nBy Junio's suggestion, we include matched remote names in the advice. It is\napplied to checkout, too.\n\nWe also fix the behavior of --guess-remote when there are multiple matches.\n\nChanges from the previous patch:\n\n * add another commit to fix the behavior of --guess-remote\n\nYoichi NAKAYAMA (4):\n  checkout: extract function to display advice for ambiguous remotes\n  checkout: improve message for ambiguous remote branch name\n  worktree add: improve message for ambiguous remote branch name\n  worktree add: treat multiple matches with --guess-remote as an error\n\n Documentation/config/worktree.adoc |  5 +-\n Documentation/git-worktree.adoc    |  4 +-\n builtin/checkout.c                 | 76 ++++++++++++++++++------------\n builtin/worktree.c                 | 57 +++++++++++++++++++---\n checkout.c                         | 14 +++++-\n checkout.h                         |  5 +-\n t/t2400-worktree-add.sh            | 17 ++++++-\n 7 files changed, 134 insertions(+), 44 deletions(-)\n\n\nbase-commit: dea0ea3582e6980ddbc1173cc8e3e9f9db91cde0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v8\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v8\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v7:\n\n 1:  e3f7d88520 = 1:  e3f7d88520 checkout: extract function to display advice for ambiguous remotes\n 2:  89c0f4d303 = 2:  89c0f4d303 checkout: improve message for ambiguous remote branch name\n 3:  095a5796d4 = 3:  095a5796d4 worktree add: improve message for ambiguous remote branch name\n -:  ---------- > 4:  927856e0a0 worktree add: treat multiple matches with --guess-remote as an error\n\n-- \ngitgitgadget\n"},{"id":"551240","messageId":"e3f7d885209e6cf9487bea296bc81df43f3758db.1787691875.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v8.git.1787691875.gitgitgadget@gmail.com","subject":"[PATCH v8 1/4] checkout: extract function to display advice for ambiguous remotes","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-25T21:04:32Z","receivedAt":"2026-08-25T21:04:41Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 62 ++++++++++++++++++++++++----------------------\n 1 file changed, 33 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 55e3a89a85..650eda735f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,6 +1343,34 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n+static void advice_disambiguating_remotes(enum checkout_command which_command)\n+{\n+\tconst char *cmdname;\n+\n+\tswitch (which_command) {\n+\tcase CHECKOUT_CHECKOUT:\n+\t\tcmdname = \"checkout\";\n+\t\tbreak;\n+\tcase CHECKOUT_SWITCH:\n+\t\tcmdname = \"switch\";\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n+\t\t    which_command);\n+\t\tbreak;\n+\t}\n+\n+\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t \"checkout.defaultRemote=origin in your config.\"),\n+\t       cmdname);\n+}\n+\n static char *parse_remote_branch(const char *arg,\n \t\t\t\t struct object_id *rev,\n \t\t\t\t int could_be_checkout_paths,\n@@ -1358,35 +1386,11 @@ static char *parse_remote_branch(const char *arg,\n \t}\n \n \tif (!remote && num_matches > 1) {\n-\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n-\t\t    const char *cmdname;\n-\n-\t\t    switch (which_command) {\n-\t\t    case CHECKOUT_CHECKOUT:\n-\t\t\t    cmdname = \"checkout\";\n-\t\t\t    break;\n-\t\t    case CHECKOUT_SWITCH:\n-\t\t\t    cmdname = \"switch\";\n-\t\t\t    break;\n-\t\t    default:\n-\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n-\t\t\t\twhich_command);\n-\t\t\t    break;\n-\t\t    }\n-\n-\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n-\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"    git %s --track origin/<name>\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n-\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n-\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n-\t\t\t   cmdname);\n-\t    }\n-\n-\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n-\t\targ, num_matches);\n+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\tadvice_disambiguating_remotes(which_command);\n+\n+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t    arg, num_matches);\n \t}\n \n \treturn remote;\n-- \ngitgitgadget\n\n"},{"id":"551241","messageId":"89c0f4d30317ebf7cda884710944e9a6f23d46fe.1787691875.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v8.git.1787691875.gitgitgadget@gmail.com","subject":"[PATCH v8 2/4] checkout: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-25T21:04:33Z","receivedAt":"2026-08-25T21:04:42Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git checkout bar-topic' without specifying a\nremote, and there is no local branch named bar-topic, we try to guess\nwhich remote branch bar-topic refers to, then create a new branch\nnamed bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we cannot determine\na single remote.\n\nTo make it easier to resolve the ambiguity, provide the names of the\nmatching remotes for the specified branch name.\n\nTo achieve that, add an optional feature to the\n`unique_tracking_name()` function that allows the matching remote\nnames to be exposed to the caller.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 28 ++++++++++++++++++++--------\n builtin/worktree.c |  4 ++--\n checkout.c         | 14 ++++++++++++--\n checkout.h         |  5 ++++-\n 4 files changed, 38 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 650eda735f..07373615d8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,9 +1343,12 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n-static void advice_disambiguating_remotes(enum checkout_command which_command)\n+static void advise_disambiguating_remotes(enum checkout_command which_command,\n+\t\t\t\t\t  const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n {\n \tconst char *cmdname;\n+\tstruct string_list_item *item;\n \n \tswitch (which_command) {\n \tcase CHECKOUT_CHECKOUT:\n@@ -1360,15 +1363,19 @@ static void advice_disambiguating_remotes(enum checkout_command which_command)\n \t\tbreak;\n \t}\n \n-\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n \t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n \t\t \"\\n\"\n-\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"    git %s --track <remote>/%s\\n\"\n \t\t \"\\n\"\n-\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n \t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n \t\t \"checkout.defaultRemote=origin in your config.\"),\n-\t       cmdname);\n+\t       cmdname, branch);\n }\n \n static char *parse_remote_branch(const char *arg,\n@@ -1377,7 +1384,10 @@ static char *parse_remote_branch(const char *arg,\n \t\t\t\t enum checkout_command which_command)\n {\n \tint num_matches = 0;\n-\tchar *remote = unique_tracking_name(arg, rev, &num_matches);\n+\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\tchar *remote = unique_tracking_name(arg, rev, &num_matches,\n+\t\t\t\t\t    &matched_remote_names);\n \n \tif (remote && could_be_checkout_paths) {\n \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n@@ -1387,12 +1397,14 @@ static char *parse_remote_branch(const char *arg,\n \n \tif (!remote && num_matches > 1) {\n \t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n-\t\t\tadvice_disambiguating_remotes(which_command);\n-\n+\t\t\tadvise_disambiguating_remotes(which_command, arg,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n \t\t    arg, num_matches);\n \t}\n \n+\tstring_list_clear(&matched_remote_names, 0);\n+\n \treturn remote;\n }\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..22c8e5e131 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -782,7 +782,7 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -904,7 +904,7 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 1588b116ee..2806b783ec 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -8,6 +8,7 @@\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n@@ -17,6 +18,7 @@ struct tracking_name_data {\n \tconst char *default_remote;\n \tchar *default_dst_ref;\n \tstruct object_id *default_dst_oid;\n+\tstruct string_list **remote_names;\n };\n \n #define TRACKING_NAME_DATA_INIT { 0 }\n@@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\toidcpy(dst, cb->dst_oid);\n \t\tcb->default_dst_oid = dst;\n \t}\n+\tif (cb->remote_names)\n+\t\tstring_list_append(*cb->remote_names, remote->name);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -48,14 +52,20 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n }\n \n char *unique_tracking_name(const char *name, struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched)\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tconst char *default_remote = NULL;\n-\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n+\n+\tif (!repo_config_get_string_tmp(the_repository,\n+\t\t\t\t\t\"checkout.defaultremote\",\n+\t\t\t\t\t&default_remote))\n \t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n+\tif (dwim_remote_names)\n+\t\tcb_data.remote_names = &dwim_remote_names;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\ndiff --git a/checkout.h b/checkout.h\nindex 55920e7aeb..0b185a0fc9 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -3,6 +3,8 @@\n \n #include \"hash.h\"\n \n+struct string_list;\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n@@ -10,6 +12,7 @@\n  */\n char *unique_tracking_name(const char *name,\n \t\t\t   struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched);\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names);\n \n #endif /* CHECKOUT_H */\n-- \ngitgitgadget\n\n"},{"id":"551242","messageId":"095a5796d4c92012825daa07eef67140a7461018.1787691875.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v8.git.1787691875.gitgitgadget@gmail.com","subject":"[PATCH v8 3/4] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-25T21:04:34Z","receivedAt":"2026-08-25T21:04:44Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add ../foo-dir bar-topic' without\nspecifying a remote, and there is no local branch named bar-topic, we\ntry to guess which remote branch bar-topic refers to, then create a\nnew branch named bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we silently gave\nup, leaving the variable 'branch' intact.  We then entered the\nconditional clause 'if (!opts.orphan &&\n!lookup_commit_reference_by_name(branch))' and triggered an \"invalid\nreference\" error.  This error message did not provide enough\ninformation to resolve the ambiguity.\n\nWhen multiple matching branches are found, display a hint and a\ndescriptive error message and die immediately.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/worktree.c      | 37 ++++++++++++++++++++++++++++++++++---\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 36 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 22c8e5e131..320b18873d 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -788,6 +788,25 @@ static char *dwim_branch(const char *path, char **new_branch)\n \treturn NULL;\n }\n \n+static void advise_disambiguating_remotes(const char *path, const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n+{\n+\tstruct string_list_item *item;\n+\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n+\t\t \"<remote>, you can do so by:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n+\t       branch, path, branch);\n+}\n+\n static int add(int ac, const char **av, const char *prefix,\n \t       struct repository *repo UNUSED)\n {\n@@ -898,17 +917,29 @@ static int add(int ac, const char **av, const char *prefix,\n \t\t/* DWIM: Infer --orphan when repo has no refs. */\n \t\topts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n \t} else if (ac == 2) {\n-\t\tstruct object_id oid;\n \t\tstruct commit *commit;\n-\t\tchar *remote;\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n+\t\t\tstruct object_id oid;\n+\t\t\tchar *remote;\n+\t\t\tint num_matches = 0;\n+\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet &&\n+\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\t\tadvise_disambiguating_remotes(path, branch,\n+\t\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n+\t\t\tstring_list_clear(&matched_remote_names, 0);\n \t\t}\n \n \t\tif (!strcmp(branch, \"HEAD\"))\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n-- \ngitgitgadget\n\n"},{"id":"551243","messageId":"927856e0a07cf6d9e7e1093a4fbf979584141224.1787691875.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v8.git.1787691875.gitgitgadget@gmail.com","subject":"[PATCH v8 4/4] worktree add: treat multiple matches with --guess-remote as an error","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-25T21:04:35Z","receivedAt":"2026-08-25T21:04:45Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen 'git worktree add <path>' is invoked without <commit-ish> and\nwith the --guess-remote option (or when worktree.guessRemote is set to\ntrue), it tries to find a remote-tracking branch matching the basename\nof <path>.\n\nCurrently, the behavior when multiple matches are found is the same as\nwhen no match is found: it falls back to creating a branch from\nHEAD. This has been the behavior since 71d6682d8c (worktree: add\n--guess-remote option to add subcommand, 2017-11-29), when the option\nwas first introduced.\n\nHowever, if the specified <path> matches any remote-tracking branch,\nwe infer that the user intended to use one of the remote-tracking\nbranches as the start-point rather than HEAD. So we abort the creation\nof the branch and worktree when there are multiple matches, and\ninstruct the user to choose the start-point.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n Documentation/config/worktree.adoc |  5 +--\n Documentation/git-worktree.adoc    |  4 ++-\n builtin/worktree.c                 | 58 ++++++++++++++++++------------\n t/t2400-worktree-add.sh            | 13 +++++++\n 4 files changed, 55 insertions(+), 25 deletions(-)\n\ndiff --git a/Documentation/config/worktree.adoc b/Documentation/config/worktree.adoc\nindex a248076ea5..0930183b91 100644\n--- a/Documentation/config/worktree.adoc\n+++ b/Documentation/config/worktree.adoc\n@@ -5,8 +5,9 @@\n \tset to true, `worktree add` tries to find a remote-tracking\n \tbranch whose name uniquely matches the new branch name.  If\n \tsuch a branch exists, it is checked out and set as \"upstream\"\n-\tfor the new branch.  If no such match can be found, it falls\n-\tback to creating a new branch from the current `HEAD`.\n+\tfor the new branch.  If multiple matches are found, the command\n+\tfails.  If no such match can be found, it falls back to\n+\tcreating a new branch from the current `HEAD`.\n \n `worktree.useRelativePaths`::\n \tLink worktrees using relative paths (when \"`true`\") or absolute\ndiff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc\nindex fbf8426cd9..32787eacc3 100644\n--- a/Documentation/git-worktree.adoc\n+++ b/Documentation/git-worktree.adoc\n@@ -219,7 +219,9 @@ To remove a locked worktree, specify `--force` twice.\n \tof creating a new branch from `HEAD`, if there exists a tracking\n \tbranch in exactly one remote matching the basename of _<path>_,\n \tbase the new branch on the remote-tracking branch, and mark\n-\tthe remote-tracking branch as \"upstream\" from the new branch.\n+\tthe remote-tracking branch as \"upstream\" from the new branch. If\n+\tthere are multiple matches, the command fails. If there is no\n+\tmatch, the command falls back to creating a new branch from `HEAD`.\n +\n This can also be set up as the default behaviour by using the\n `worktree.guessRemote` config option.\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 320b18873d..07163bf9b7 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -764,7 +764,26 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)\n \treturn 1;\n }\n \n-static char *dwim_branch(const char *path, char **new_branch)\n+static void advise_disambiguating_remotes(const char *path, const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n+{\n+\tstruct string_list_item *item;\n+\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n+\t\t \"<remote>, you can do so by:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n+\t       branch, path, branch);\n+}\n+\n+static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n {\n \tint n;\n \tint branch_exists;\n@@ -782,31 +801,26 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n+\t\tchar *remote;\n+\t\tint num_matches = 0;\n+\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\tremote = unique_tracking_name(*new_branch, &oid, &num_matches,\n+\t\t\t\t\t      &matched_remote_names);\n+\t\tif (!remote && num_matches > 1) {\n+\t\t\tif (!opts->quiet &&\n+\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\tadvise_disambiguating_remotes(path, *new_branch,\n+\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t    *new_branch, num_matches);\n+\t\t}\n+\t\tstring_list_clear(&matched_remote_names, 0);\n \t\treturn remote;\n \t}\n \treturn NULL;\n }\n \n-static void advise_disambiguating_remotes(const char *path, const char *branch,\n-\t\t\t\t\t  const struct string_list *matched_remote_names)\n-{\n-\tstruct string_list_item *item;\n-\n-\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n-\tfor_each_string_list_item(item, matched_remote_names) {\n-\t\tadvise(_(\"  %s\"), item->string);\n-\t}\n-\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n-\t\t \"<remote>, you can do so by:\\n\"\n-\t\t \"\\n\"\n-\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n-\t\t \"\\n\"\n-\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n-\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n-\t       branch, path, branch);\n-}\n-\n static int add(int ac, const char **av, const char *prefix,\n \t       struct repository *repo UNUSED)\n {\n@@ -909,7 +923,7 @@ static int add(int ac, const char **av, const char *prefix,\n \t\topts.orphan = dwim_orphan(&opts, !!opt_track, 0);\n \t} else if (ac < 2) {\n \t\t/* DWIM: Guess branch name from path. */\n-\t\tchar *s = dwim_branch(path, &new_branch_to_free);\n+\t\tchar *s = dwim_branch(&opts, path, &new_branch_to_free);\n \t\tif (s)\n \t\t\tbranch = branch_to_free = s;\n \t\tnew_branch = new_branch_to_free;\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 5c105cf252..a37137042d 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -669,6 +669,19 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '\n \t\ttest_cmp_rev refs/remotes/repo_a/foo refs/heads/foo\n \t)\n '\n+\n+test_expect_success 'git worktree add --guess-remote fails if there are multiple matches' '\n+\ttest_when_finished rm -rf repo_a repo_b foo &&\n+\tsetup_remote_repo repo_a repo_b &&\n+\t(\n+\t\tcd repo_b &&\n+\t\tgit remote add repo_a2 ../repo_a &&\n+\t\tgit fetch repo_a2 &&\n+\t\ttest_must_fail git worktree add --guess-remote ../foo 2>actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" actual\n+\t)\n+'\n+\n test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \ngitgitgadget\n"},{"id":"551248","messageId":"xmqqcxv5lwe8.fsf@gitster.g","threadId":"66141","inReplyTo":"927856e0a07cf6d9e7e1093a4fbf979584141224.1787691875.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v8 4/4] worktree add: treat multiple matches with --guess-remote as an error","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-25T21:31:43Z","receivedAt":"2026-08-25T21:31:46Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +static void advise_disambiguating_remotes(const char *path, const char *branch,\n> +\t\t\t\t\t  const struct string_list *matched_remote_names)\n> +{\n> +\tstruct string_list_item *item;\n> +\n> +\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n> +\tfor_each_string_list_item(item, matched_remote_names) {\n> +\t\tadvise(_(\"  %s\"), item->string);\n> +\t}\n> +\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n> +\t\t \"<remote>, you can do so by:\\n\"\n> +\t\t \"\\n\"\n> +\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n> +\t\t \"\\n\"\n> +\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n> +\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n> +\t       branch, path, branch);\n> +}\n\nWasn't this function added in this series somewhere earlier in the\ntopic?  If we add it high enough when we did so, we wouldn't have to\nmove it higher like this patch does.\n\n> +static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n>  {\n>  \tint n;\n>  \tint branch_exists;\n> @@ -782,31 +801,26 @@ static char *dwim_branch(const char *path, char **new_branch)\n>  \t*new_branch = branchname;\n>  \tif (guess_remote) {\n>  \t\tstruct object_id oid;\n> -\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n> +\t\tchar *remote;\n> +\t\tint num_matches = 0;\n> +\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n> +\n> +\t\tremote = unique_tracking_name(*new_branch, &oid, &num_matches,\n> +\t\t\t\t\t      &matched_remote_names);\n> +\t\tif (!remote && num_matches > 1) {\n> +\t\t\tif (!opts->quiet &&\n> +\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n> +\t\t\t\tadvise_disambiguating_remotes(path, *new_branch,\n> +\t\t\t\t\t\t\t      &matched_remote_names);\n> +\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n> +\t\t\t    *new_branch, num_matches);\n> +\t\t}\n> +\t\tstring_list_clear(&matched_remote_names, 0);\n>  \t\treturn remote;\n>  \t}\n>  \treturn NULL;\n>  }\n\nLooking good.\n\nThanks.\n\n"},{"id":"551276","messageId":"pull.2197.v9.git.1787741111.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v9 0/4] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-26T10:45:06Z","receivedAt":"2026-08-26T10:45:14Z","isPatch":true,"body":"'git worktree add ../foo-dir bar-topic' fails to dwim when there are\nmultiple remote branches with name `bar-topic'. But it doesn't display\nmeaningful message as 'git checkout bar-topic' does under the same\nsituation.\n\nWe improve this by adding advice and modify the error message for worktree\nadd.\n\nBy Junio's suggestion, we include matched remote names in the advice. It is\napplied to checkout, too.\n\nWe also fix the behavior of --guess-remote when there are multiple matches.\n\nChanges from the previous patch:\n\n * move advise_disambiguating_remotes() definition upper in the 3rd commit\n\nYoichi NAKAYAMA (4):\n  checkout: extract function to display advice for ambiguous remotes\n  checkout: improve message for ambiguous remote branch name\n  worktree add: improve message for ambiguous remote branch name\n  worktree add: treat multiple matches with --guess-remote as an error\n\n Documentation/config/worktree.adoc |  5 +-\n Documentation/git-worktree.adoc    |  4 +-\n builtin/checkout.c                 | 76 ++++++++++++++++++------------\n builtin/worktree.c                 | 57 +++++++++++++++++++---\n checkout.c                         | 14 +++++-\n checkout.h                         |  5 +-\n t/t2400-worktree-add.sh            | 17 ++++++-\n 7 files changed, 134 insertions(+), 44 deletions(-)\n\n\nbase-commit: dea0ea3582e6980ddbc1173cc8e3e9f9db91cde0\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v9\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v9\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v8:\n\n 1:  e3f7d88520 = 1:  e3f7d88520 checkout: extract function to display advice for ambiguous remotes\n 2:  89c0f4d303 = 2:  89c0f4d303 checkout: improve message for ambiguous remote branch name\n 3:  095a5796d4 ! 3:  1010ac3295 worktree add: improve message for ambiguous remote branch name\n     @@ Commit message\n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n       ## builtin/worktree.c ##\n     -@@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch)\n     - \treturn NULL;\n     +@@ builtin/worktree.c: static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)\n     + \treturn 1;\n       }\n       \n      +static void advise_disambiguating_remotes(const char *path, const char *branch,\n     @@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch\n      +\t       branch, path, branch);\n      +}\n      +\n     - static int add(int ac, const char **av, const char *prefix,\n     - \t       struct repository *repo UNUSED)\n     + static char *dwim_branch(const char *path, char **new_branch)\n       {\n     + \tint n;\n      @@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n       \t\t/* DWIM: Infer --orphan when repo has no refs. */\n       \t\topts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n 4:  927856e0a0 ! 4:  edb88b658a worktree add: treat multiple matches with --guess-remote as an error\n     @@ Documentation/git-worktree.adoc: To remove a locked worktree, specify `--force`\n       `worktree.guessRemote` config option.\n      \n       ## builtin/worktree.c ##\n     -@@ builtin/worktree.c: static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)\n     - \treturn 1;\n     +@@ builtin/worktree.c: static void advise_disambiguating_remotes(const char *path, const char *branch,\n     + \t       branch, path, branch);\n       }\n       \n      -static char *dwim_branch(const char *path, char **new_branch)\n     -+static void advise_disambiguating_remotes(const char *path, const char *branch,\n     -+\t\t\t\t\t  const struct string_list *matched_remote_names)\n     -+{\n     -+\tstruct string_list_item *item;\n     -+\n     -+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n     -+\tfor_each_string_list_item(item, matched_remote_names) {\n     -+\t\tadvise(_(\"  %s\"), item->string);\n     -+\t}\n     -+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n     -+\t\t \"<remote>, you can do so by:\\n\"\n     -+\t\t \"\\n\"\n     -+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n     -+\t\t \"\\n\"\n     -+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n     -+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n     -+\t       branch, path, branch);\n     -+}\n     -+\n      +static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n       {\n       \tint n;\n     @@ builtin/worktree.c: static char *dwim_branch(const char *path, char **new_branch\n       \t\treturn remote;\n       \t}\n       \treturn NULL;\n     - }\n     - \n     --static void advise_disambiguating_remotes(const char *path, const char *branch,\n     --\t\t\t\t\t  const struct string_list *matched_remote_names)\n     --{\n     --\tstruct string_list_item *item;\n     --\n     --\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n     --\tfor_each_string_list_item(item, matched_remote_names) {\n     --\t\tadvise(_(\"  %s\"), item->string);\n     --\t}\n     --\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n     --\t\t \"<remote>, you can do so by:\\n\"\n     --\t\t \"\\n\"\n     --\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n     --\t\t \"\\n\"\n     --\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n     --\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n     --\t       branch, path, branch);\n     --}\n     --\n     - static int add(int ac, const char **av, const char *prefix,\n     - \t       struct repository *repo UNUSED)\n     - {\n      @@ builtin/worktree.c: static int add(int ac, const char **av, const char *prefix,\n       \t\topts.orphan = dwim_orphan(&opts, !!opt_track, 0);\n       \t} else if (ac < 2) {\n\n-- \ngitgitgadget\n"},{"id":"551277","messageId":"e3f7d885209e6cf9487bea296bc81df43f3758db.1787741111.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v9.git.1787741111.gitgitgadget@gmail.com","subject":"[PATCH v9 1/4] checkout: extract function to display advice for ambiguous remotes","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-26T10:45:07Z","receivedAt":"2026-08-26T10:45:17Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 62 ++++++++++++++++++++++++----------------------\n 1 file changed, 33 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 55e3a89a85..650eda735f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,6 +1343,34 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n+static void advice_disambiguating_remotes(enum checkout_command which_command)\n+{\n+\tconst char *cmdname;\n+\n+\tswitch (which_command) {\n+\tcase CHECKOUT_CHECKOUT:\n+\t\tcmdname = \"checkout\";\n+\t\tbreak;\n+\tcase CHECKOUT_SWITCH:\n+\t\tcmdname = \"switch\";\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n+\t\t    which_command);\n+\t\tbreak;\n+\t}\n+\n+\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t \"checkout.defaultRemote=origin in your config.\"),\n+\t       cmdname);\n+}\n+\n static char *parse_remote_branch(const char *arg,\n \t\t\t\t struct object_id *rev,\n \t\t\t\t int could_be_checkout_paths,\n@@ -1358,35 +1386,11 @@ static char *parse_remote_branch(const char *arg,\n \t}\n \n \tif (!remote && num_matches > 1) {\n-\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n-\t\t    const char *cmdname;\n-\n-\t\t    switch (which_command) {\n-\t\t    case CHECKOUT_CHECKOUT:\n-\t\t\t    cmdname = \"checkout\";\n-\t\t\t    break;\n-\t\t    case CHECKOUT_SWITCH:\n-\t\t\t    cmdname = \"switch\";\n-\t\t\t    break;\n-\t\t    default:\n-\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n-\t\t\t\twhich_command);\n-\t\t\t    break;\n-\t\t    }\n-\n-\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n-\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"    git %s --track origin/<name>\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n-\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n-\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n-\t\t\t   cmdname);\n-\t    }\n-\n-\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n-\t\targ, num_matches);\n+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\tadvice_disambiguating_remotes(which_command);\n+\n+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t    arg, num_matches);\n \t}\n \n \treturn remote;\n-- \ngitgitgadget\n\n"},{"id":"551279","messageId":"89c0f4d30317ebf7cda884710944e9a6f23d46fe.1787741111.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v9.git.1787741111.gitgitgadget@gmail.com","subject":"[PATCH v9 2/4] checkout: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-26T10:45:08Z","receivedAt":"2026-08-26T10:45:21Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git checkout bar-topic' without specifying a\nremote, and there is no local branch named bar-topic, we try to guess\nwhich remote branch bar-topic refers to, then create a new branch\nnamed bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we cannot determine\na single remote.\n\nTo make it easier to resolve the ambiguity, provide the names of the\nmatching remotes for the specified branch name.\n\nTo achieve that, add an optional feature to the\n`unique_tracking_name()` function that allows the matching remote\nnames to be exposed to the caller.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 28 ++++++++++++++++++++--------\n builtin/worktree.c |  4 ++--\n checkout.c         | 14 ++++++++++++--\n checkout.h         |  5 ++++-\n 4 files changed, 38 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 650eda735f..07373615d8 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,9 +1343,12 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n-static void advice_disambiguating_remotes(enum checkout_command which_command)\n+static void advise_disambiguating_remotes(enum checkout_command which_command,\n+\t\t\t\t\t  const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n {\n \tconst char *cmdname;\n+\tstruct string_list_item *item;\n \n \tswitch (which_command) {\n \tcase CHECKOUT_CHECKOUT:\n@@ -1360,15 +1363,19 @@ static void advice_disambiguating_remotes(enum checkout_command which_command)\n \t\tbreak;\n \t}\n \n-\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n \t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n \t\t \"\\n\"\n-\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"    git %s --track <remote>/%s\\n\"\n \t\t \"\\n\"\n-\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n \t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n \t\t \"checkout.defaultRemote=origin in your config.\"),\n-\t       cmdname);\n+\t       cmdname, branch);\n }\n \n static char *parse_remote_branch(const char *arg,\n@@ -1377,7 +1384,10 @@ static char *parse_remote_branch(const char *arg,\n \t\t\t\t enum checkout_command which_command)\n {\n \tint num_matches = 0;\n-\tchar *remote = unique_tracking_name(arg, rev, &num_matches);\n+\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\tchar *remote = unique_tracking_name(arg, rev, &num_matches,\n+\t\t\t\t\t    &matched_remote_names);\n \n \tif (remote && could_be_checkout_paths) {\n \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n@@ -1387,12 +1397,14 @@ static char *parse_remote_branch(const char *arg,\n \n \tif (!remote && num_matches > 1) {\n \t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n-\t\t\tadvice_disambiguating_remotes(which_command);\n-\n+\t\t\tadvise_disambiguating_remotes(which_command, arg,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n \t\t    arg, num_matches);\n \t}\n \n+\tstring_list_clear(&matched_remote_names, 0);\n+\n \treturn remote;\n }\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..22c8e5e131 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -782,7 +782,7 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -904,7 +904,7 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 1588b116ee..2806b783ec 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -8,6 +8,7 @@\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n@@ -17,6 +18,7 @@ struct tracking_name_data {\n \tconst char *default_remote;\n \tchar *default_dst_ref;\n \tstruct object_id *default_dst_oid;\n+\tstruct string_list **remote_names;\n };\n \n #define TRACKING_NAME_DATA_INIT { 0 }\n@@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\toidcpy(dst, cb->dst_oid);\n \t\tcb->default_dst_oid = dst;\n \t}\n+\tif (cb->remote_names)\n+\t\tstring_list_append(*cb->remote_names, remote->name);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -48,14 +52,20 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n }\n \n char *unique_tracking_name(const char *name, struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched)\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tconst char *default_remote = NULL;\n-\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n+\n+\tif (!repo_config_get_string_tmp(the_repository,\n+\t\t\t\t\t\"checkout.defaultremote\",\n+\t\t\t\t\t&default_remote))\n \t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n+\tif (dwim_remote_names)\n+\t\tcb_data.remote_names = &dwim_remote_names;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\ndiff --git a/checkout.h b/checkout.h\nindex 55920e7aeb..0b185a0fc9 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -3,6 +3,8 @@\n \n #include \"hash.h\"\n \n+struct string_list;\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n@@ -10,6 +12,7 @@\n  */\n char *unique_tracking_name(const char *name,\n \t\t\t   struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched);\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names);\n \n #endif /* CHECKOUT_H */\n-- \ngitgitgadget\n\n"},{"id":"551278","messageId":"1010ac32958dffab2b23f636305fe8de7fb5bba2.1787741111.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v9.git.1787741111.gitgitgadget@gmail.com","subject":"[PATCH v9 3/4] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-26T10:45:09Z","receivedAt":"2026-08-26T10:45:24Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add ../foo-dir bar-topic' without\nspecifying a remote, and there is no local branch named bar-topic, we\ntry to guess which remote branch bar-topic refers to, then create a\nnew branch named bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we silently gave\nup, leaving the variable 'branch' intact.  We then entered the\nconditional clause 'if (!opts.orphan &&\n!lookup_commit_reference_by_name(branch))' and triggered an \"invalid\nreference\" error.  This error message did not provide enough\ninformation to resolve the ambiguity.\n\nWhen multiple matching branches are found, display a hint and a\ndescriptive error message and die immediately.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/worktree.c      | 37 ++++++++++++++++++++++++++++++++++---\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 36 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 22c8e5e131..c745deddde 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -764,6 +764,25 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)\n \treturn 1;\n }\n \n+static void advise_disambiguating_remotes(const char *path, const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n+{\n+\tstruct string_list_item *item;\n+\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n+\t\t \"<remote>, you can do so by:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n+\t       branch, path, branch);\n+}\n+\n static char *dwim_branch(const char *path, char **new_branch)\n {\n \tint n;\n@@ -898,17 +917,29 @@ static int add(int ac, const char **av, const char *prefix,\n \t\t/* DWIM: Infer --orphan when repo has no refs. */\n \t\topts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n \t} else if (ac == 2) {\n-\t\tstruct object_id oid;\n \t\tstruct commit *commit;\n-\t\tchar *remote;\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n+\t\t\tstruct object_id oid;\n+\t\t\tchar *remote;\n+\t\t\tint num_matches = 0;\n+\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet &&\n+\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\t\tadvise_disambiguating_remotes(path, branch,\n+\t\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n+\t\t\tstring_list_clear(&matched_remote_names, 0);\n \t\t}\n \n \t\tif (!strcmp(branch, \"HEAD\"))\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n-- \ngitgitgadget\n\n"},{"id":"551280","messageId":"edb88b658a748e812a6fe2850b8a0f92ffcbb629.1787741111.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v9.git.1787741111.gitgitgadget@gmail.com","subject":"[PATCH v9 4/4] worktree add: treat multiple matches with --guess-remote as an error","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-26T10:45:10Z","receivedAt":"2026-08-26T10:45:27Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen 'git worktree add <path>' is invoked without <commit-ish> and\nwith the --guess-remote option (or when worktree.guessRemote is set to\ntrue), it tries to find a remote-tracking branch matching the basename\nof <path>.\n\nCurrently, the behavior when multiple matches are found is the same as\nwhen no match is found: it falls back to creating a branch from\nHEAD. This has been the behavior since 71d6682d8c (worktree: add\n--guess-remote option to add subcommand, 2017-11-29), when the option\nwas first introduced.\n\nHowever, if the specified <path> matches any remote-tracking branch,\nwe infer that the user intended to use one of the remote-tracking\nbranches as the start-point rather than HEAD. So we abort the creation\nof the branch and worktree when there are multiple matches, and\ninstruct the user to choose the start-point.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n Documentation/config/worktree.adoc |  5 +++--\n Documentation/git-worktree.adoc    |  4 +++-\n builtin/worktree.c                 | 20 +++++++++++++++++---\n t/t2400-worktree-add.sh            | 13 +++++++++++++\n 4 files changed, 36 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/worktree.adoc b/Documentation/config/worktree.adoc\nindex a248076ea5..0930183b91 100644\n--- a/Documentation/config/worktree.adoc\n+++ b/Documentation/config/worktree.adoc\n@@ -5,8 +5,9 @@\n \tset to true, `worktree add` tries to find a remote-tracking\n \tbranch whose name uniquely matches the new branch name.  If\n \tsuch a branch exists, it is checked out and set as \"upstream\"\n-\tfor the new branch.  If no such match can be found, it falls\n-\tback to creating a new branch from the current `HEAD`.\n+\tfor the new branch.  If multiple matches are found, the command\n+\tfails.  If no such match can be found, it falls back to\n+\tcreating a new branch from the current `HEAD`.\n \n `worktree.useRelativePaths`::\n \tLink worktrees using relative paths (when \"`true`\") or absolute\ndiff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc\nindex fbf8426cd9..32787eacc3 100644\n--- a/Documentation/git-worktree.adoc\n+++ b/Documentation/git-worktree.adoc\n@@ -219,7 +219,9 @@ To remove a locked worktree, specify `--force` twice.\n \tof creating a new branch from `HEAD`, if there exists a tracking\n \tbranch in exactly one remote matching the basename of _<path>_,\n \tbase the new branch on the remote-tracking branch, and mark\n-\tthe remote-tracking branch as \"upstream\" from the new branch.\n+\tthe remote-tracking branch as \"upstream\" from the new branch. If\n+\tthere are multiple matches, the command fails. If there is no\n+\tmatch, the command falls back to creating a new branch from `HEAD`.\n +\n This can also be set up as the default behaviour by using the\n `worktree.guessRemote` config option.\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex c745deddde..07163bf9b7 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -783,7 +783,7 @@ static void advise_disambiguating_remotes(const char *path, const char *branch,\n \t       branch, path, branch);\n }\n \n-static char *dwim_branch(const char *path, char **new_branch)\n+static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n {\n \tint n;\n \tint branch_exists;\n@@ -801,7 +801,21 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n+\t\tchar *remote;\n+\t\tint num_matches = 0;\n+\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\tremote = unique_tracking_name(*new_branch, &oid, &num_matches,\n+\t\t\t\t\t      &matched_remote_names);\n+\t\tif (!remote && num_matches > 1) {\n+\t\t\tif (!opts->quiet &&\n+\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\tadvise_disambiguating_remotes(path, *new_branch,\n+\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t    *new_branch, num_matches);\n+\t\t}\n+\t\tstring_list_clear(&matched_remote_names, 0);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -909,7 +923,7 @@ static int add(int ac, const char **av, const char *prefix,\n \t\topts.orphan = dwim_orphan(&opts, !!opt_track, 0);\n \t} else if (ac < 2) {\n \t\t/* DWIM: Guess branch name from path. */\n-\t\tchar *s = dwim_branch(path, &new_branch_to_free);\n+\t\tchar *s = dwim_branch(&opts, path, &new_branch_to_free);\n \t\tif (s)\n \t\t\tbranch = branch_to_free = s;\n \t\tnew_branch = new_branch_to_free;\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 5c105cf252..a37137042d 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -669,6 +669,19 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '\n \t\ttest_cmp_rev refs/remotes/repo_a/foo refs/heads/foo\n \t)\n '\n+\n+test_expect_success 'git worktree add --guess-remote fails if there are multiple matches' '\n+\ttest_when_finished rm -rf repo_a repo_b foo &&\n+\tsetup_remote_repo repo_a repo_b &&\n+\t(\n+\t\tcd repo_b &&\n+\t\tgit remote add repo_a2 ../repo_a &&\n+\t\tgit fetch repo_a2 &&\n+\t\ttest_must_fail git worktree add --guess-remote ../foo 2>actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" actual\n+\t)\n+'\n+\n test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \ngitgitgadget\n"},{"id":"551289","messageId":"xmqq4iggkiq2.fsf@gitster.g","threadId":"66141","inReplyTo":"e3f7d885209e6cf9487bea296bc81df43f3758db.1787741111.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v9 1/4] checkout: extract function to display advice for ambiguous remotes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-26T15:24:37Z","receivedAt":"2026-08-26T15:24:40Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n>\n> Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nDon't we want to describe a bit of explanation between these two\nlines?  There are a few things that immediately come to mind as\nmotivation for such changes, like that the original place was too\ndeeply nested, that the original code was incorrectly indented, and\nthat we are going to extend this function in later steps in the\nseries.\n\n> +static void advice_disambiguating_remotes(enum checkout_command which_command)\n> +{\n\n[2/4] updates this function to a better name; let's give it that\nname from the beginning.\n"},{"id":"551295","messageId":"xmqqmru8j0dq.fsf@gitster.g","threadId":"66141","inReplyTo":"89c0f4d30317ebf7cda884710944e9a6f23d46fe.1787741111.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v9 2/4] checkout: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-26T16:46:09Z","receivedAt":"2026-08-26T16:46:22Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> diff --git a/checkout.c b/checkout.c\n> index 1588b116ee..2806b783ec 100644\n> --- a/checkout.c\n> +++ b/checkout.c\n> @@ -8,6 +8,7 @@\n>  #include \"checkout.h\"\n>  #include \"config.h\"\n>  #include \"strbuf.h\"\n> +#include \"string-list.h\"\n>  \n>  struct tracking_name_data {\n>  \t/* const */ char *src_ref;\n> @@ -17,6 +18,7 @@ struct tracking_name_data {\n>  \tconst char *default_remote;\n>  \tchar *default_dst_ref;\n>  \tstruct object_id *default_dst_oid;\n> +\tstruct string_list **remote_names;\n>  };\n\nDo we really need double indirection?  The unique_tracking_name()\nfunction that uses this struct for callback receives a pointer to\nthe string list the caller has, and the job of the callback is to\nappend to the supplied string list.  \n\nIt is not like it wants to swap the given pointer to a string list\nto the pointer to another string list, so I do not see a reason for\nanybody involved in the callchain to want this as a pointer to a\npointer.\n\n>  #define TRACKING_NAME_DATA_INIT { 0 }\n> @@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n>  \t\toidcpy(dst, cb->dst_oid);\n>  \t\tcb->default_dst_oid = dst;\n>  \t}\n> +\tif (cb->remote_names)\n> +\t\tstring_list_append(*cb->remote_names, remote->name);\n\nIf we lose the double indirection, this can become\n\n\t\tstring_list_append(cb->remote_names, remote->name);\n\n>  char *unique_tracking_name(const char *name, struct object_id *oid,\n> -\t\t\t   int *dwim_remotes_matched)\n> +\t\t\t   int *dwim_remotes_matched,\n> +\t\t\t   struct string_list *dwim_remote_names)\n>  {\n>  \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n>  \tconst char *default_remote = NULL;\n> -\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n> +\n> +\tif (!repo_config_get_string_tmp(the_repository,\n> +\t\t\t\t\t\"checkout.defaultremote\",\n> +\t\t\t\t\t&default_remote))\n>  \t\tcb_data.default_remote = default_remote;\n>  \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n>  \tcb_data.dst_oid = oid;\n> +\tif (dwim_remote_names)\n> +\t\tcb_data.remote_names = &dwim_remote_names;\n\nAnd this can become\n\n\t\tcb_data.remote_names = dwim_remote_names;\n\n"},{"id":"551363","messageId":"pull.2197.v10.git.1787841717.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.git.1786177301832.gitgitgadget@gmail.com","subject":"[PATCH v10 0/4] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-27T14:41:53Z","receivedAt":"2026-08-27T14:42:01Z","isPatch":true,"body":"'git worktree add ../foo-dir bar-topic' fails to dwim when there are\nmultiple remote branches with name `bar-topic'. But it doesn't display\nmeaningful message as 'git checkout bar-topic' does under the same\nsituation.\n\nWe improve this by adding advice and modify the error message for worktree\nadd.\n\nBy Junio's suggestion, we include matched remote names in the advice. It is\napplied to checkout, too.\n\nWe also fix the behavior of --guess-remote when there are multiple matches.\n\nChanges from the previous patch:\n\n * [1/4] fix the function name and add detailed commit message\n * [2/4] change type of tracking_name_data.remote_names\n\nYoichi NAKAYAMA (4):\n  checkout: extract function to display advice for ambiguous remotes\n  checkout: improve message for ambiguous remote branch name\n  worktree add: improve message for ambiguous remote branch name\n  worktree add: treat multiple matches with --guess-remote as an error\n\n Documentation/config/worktree.adoc |  5 +-\n Documentation/git-worktree.adoc    |  4 +-\n builtin/checkout.c                 | 76 ++++++++++++++++++------------\n builtin/worktree.c                 | 57 +++++++++++++++++++---\n checkout.c                         | 13 ++++-\n checkout.h                         |  5 +-\n t/t2400-worktree-add.sh            | 17 ++++++-\n 7 files changed, 133 insertions(+), 44 deletions(-)\n\n\nbase-commit: f78ce2f7b6df702f93d40b85d6bda92a3f65da79\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-2197%2Fyoichi%2Fimprove-worktree-add-error-message-v10\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2197/yoichi/improve-worktree-add-error-message-v10\nPull-Request: https://github.com/gitgitgadget/git/pull/2197\n\nRange-diff vs v9:\n\n 1:  e3f7d88520 ! 1:  7650c38d6b checkout: extract function to display advice for ambiguous remotes\n     @@ Metadata\n       ## Commit message ##\n          checkout: extract function to display advice for ambiguous remotes\n      \n     +    Fix incorrect indentation and reduce nesting. We are going to extend\n     +    this function in subsequent commits.\n     +\n          Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n      \n       ## builtin/checkout.c ##\n     @@ builtin/checkout.c: enum checkout_command {\n       \tCHECKOUT_RESTORE = 3,\n       };\n       \n     -+static void advice_disambiguating_remotes(enum checkout_command which_command)\n     ++static void advise_disambiguating_remotes(enum checkout_command which_command)\n      +{\n      +\tconst char *cmdname;\n      +\n     @@ builtin/checkout.c: enum checkout_command {\n      +\t\tcmdname = \"switch\";\n      +\t\tbreak;\n      +\tdefault:\n     -+\t\tBUG(\"command <%d> should not reach parse_remote_branch\",\n     ++\t\tBUG(\"command <%d> should not reach advise_disambiguating_remotes\",\n      +\t\t    which_command);\n      +\t\tbreak;\n      +\t}\n     @@ builtin/checkout.c: static char *parse_remote_branch(const char *arg,\n      -\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n      -\t\targ, num_matches);\n      +\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n     -+\t\t\tadvice_disambiguating_remotes(which_command);\n     ++\t\t\tadvise_disambiguating_remotes(which_command);\n      +\n      +\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n      +\t\t    arg, num_matches);\n 2:  89c0f4d303 ! 2:  c37c9c237a checkout: improve message for ambiguous remote branch name\n     @@ builtin/checkout.c: enum checkout_command {\n       \tCHECKOUT_RESTORE = 3,\n       };\n       \n     --static void advice_disambiguating_remotes(enum checkout_command which_command)\n     +-static void advise_disambiguating_remotes(enum checkout_command which_command)\n      +static void advise_disambiguating_remotes(enum checkout_command which_command,\n      +\t\t\t\t\t  const char *branch,\n      +\t\t\t\t\t  const struct string_list *matched_remote_names)\n     @@ builtin/checkout.c: enum checkout_command {\n       \n       \tswitch (which_command) {\n       \tcase CHECKOUT_CHECKOUT:\n     -@@ builtin/checkout.c: static void advice_disambiguating_remotes(enum checkout_command which_command)\n     +@@ builtin/checkout.c: static void advise_disambiguating_remotes(enum checkout_command which_command)\n       \t\tbreak;\n       \t}\n       \n     @@ builtin/checkout.c: static char *parse_remote_branch(const char *arg,\n       \n       \tif (!remote && num_matches > 1) {\n       \t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n     --\t\t\tadvice_disambiguating_remotes(which_command);\n     +-\t\t\tadvise_disambiguating_remotes(which_command);\n      -\n      +\t\t\tadvise_disambiguating_remotes(which_command, arg,\n      +\t\t\t\t\t\t      &matched_remote_names);\n     @@ checkout.c: struct tracking_name_data {\n       \tconst char *default_remote;\n       \tchar *default_dst_ref;\n       \tstruct object_id *default_dst_oid;\n     -+\tstruct string_list **remote_names;\n     ++\tstruct string_list *remote_names;\n       };\n       \n       #define TRACKING_NAME_DATA_INIT { 0 }\n     @@ checkout.c: static int check_tracking_name(struct remote *remote, void *cb_data)\n       \t\tcb->default_dst_oid = dst;\n       \t}\n      +\tif (cb->remote_names)\n     -+\t\tstring_list_append(*cb->remote_names, remote->name);\n     ++\t\tstring_list_append(cb->remote_names, remote->name);\n       \tif (cb->dst_ref) {\n       \t\tfree(query.dst);\n       \t\treturn 0;\n     @@ checkout.c: static int check_tracking_name(struct remote *remote, void *cb_data)\n       \t\tcb_data.default_remote = default_remote;\n       \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n       \tcb_data.dst_oid = oid;\n     -+\tif (dwim_remote_names)\n     -+\t\tcb_data.remote_names = &dwim_remote_names;\n     ++\tcb_data.remote_names = dwim_remote_names;\n       \tfor_each_remote(check_tracking_name, &cb_data);\n       \tif (dwim_remotes_matched)\n       \t\t*dwim_remotes_matched = cb_data.num_matches;\n 3:  1010ac3295 = 3:  35814b47a4 worktree add: improve message for ambiguous remote branch name\n 4:  edb88b658a = 4:  407c53b33c worktree add: treat multiple matches with --guess-remote as an error\n\n-- \ngitgitgadget\n"},{"id":"551361","messageId":"7650c38d6bf4c68cae4894d1cad5d96dd55e4b92.1787841717.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v10.git.1787841717.gitgitgadget@gmail.com","subject":"[PATCH v10 1/4] checkout: extract function to display advice for ambiguous remotes","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-27T14:41:54Z","receivedAt":"2026-08-27T14:42:02Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nFix incorrect indentation and reduce nesting. We are going to extend\nthis function in subsequent commits.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 62 ++++++++++++++++++++++++----------------------\n 1 file changed, 33 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 55e3a89a85..3cd5fff709 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,6 +1343,34 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n+static void advise_disambiguating_remotes(enum checkout_command which_command)\n+{\n+\tconst char *cmdname;\n+\n+\tswitch (which_command) {\n+\tcase CHECKOUT_CHECKOUT:\n+\t\tcmdname = \"checkout\";\n+\t\tbreak;\n+\tcase CHECKOUT_SWITCH:\n+\t\tcmdname = \"switch\";\n+\t\tbreak;\n+\tdefault:\n+\t\tBUG(\"command <%d> should not reach advise_disambiguating_remotes\",\n+\t\t    which_command);\n+\t\tbreak;\n+\t}\n+\n+\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n+\t\t \"checkout.defaultRemote=origin in your config.\"),\n+\t       cmdname);\n+}\n+\n static char *parse_remote_branch(const char *arg,\n \t\t\t\t struct object_id *rev,\n \t\t\t\t int could_be_checkout_paths,\n@@ -1358,35 +1386,11 @@ static char *parse_remote_branch(const char *arg,\n \t}\n \n \tif (!remote && num_matches > 1) {\n-\t    if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {\n-\t\t    const char *cmdname;\n-\n-\t\t    switch (which_command) {\n-\t\t    case CHECKOUT_CHECKOUT:\n-\t\t\t    cmdname = \"checkout\";\n-\t\t\t    break;\n-\t\t    case CHECKOUT_SWITCH:\n-\t\t\t    cmdname = \"switch\";\n-\t\t\t    break;\n-\t\t    default:\n-\t\t\t    BUG(\"command <%d> should not reach parse_remote_branch\",\n-\t\t\t\twhich_command);\n-\t\t\t    break;\n-\t\t    }\n-\n-\t\t    advise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n-\t\t\t     \"you can do so by fully qualifying the name with the --track option:\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"    git %s --track origin/<name>\\n\"\n-\t\t\t     \"\\n\"\n-\t\t\t     \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n-\t\t\t     \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n-\t\t\t     \"checkout.defaultRemote=origin in your config.\"),\n-\t\t\t   cmdname);\n-\t    }\n-\n-\t    die(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n-\t\targ, num_matches);\n+\t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\tadvise_disambiguating_remotes(which_command);\n+\n+\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t    arg, num_matches);\n \t}\n \n \treturn remote;\n-- \ngitgitgadget\n\n"},{"id":"551362","messageId":"c37c9c237ab3f0b99e9fc60d650b961667a7d84b.1787841717.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v10.git.1787841717.gitgitgadget@gmail.com","subject":"[PATCH v10 2/4] checkout: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-27T14:41:55Z","receivedAt":"2026-08-27T14:42:03Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git checkout bar-topic' without specifying a\nremote, and there is no local branch named bar-topic, we try to guess\nwhich remote branch bar-topic refers to, then create a new branch\nnamed bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we cannot determine\na single remote.\n\nTo make it easier to resolve the ambiguity, provide the names of the\nmatching remotes for the specified branch name.\n\nTo achieve that, add an optional feature to the\n`unique_tracking_name()` function that allows the matching remote\nnames to be exposed to the caller.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/checkout.c | 28 ++++++++++++++++++++--------\n builtin/worktree.c |  4 ++--\n checkout.c         | 13 +++++++++++--\n checkout.h         |  5 ++++-\n 4 files changed, 37 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 3cd5fff709..2bc21aa49b 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1343,9 +1343,12 @@ enum checkout_command {\n \tCHECKOUT_RESTORE = 3,\n };\n \n-static void advise_disambiguating_remotes(enum checkout_command which_command)\n+static void advise_disambiguating_remotes(enum checkout_command which_command,\n+\t\t\t\t\t  const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n {\n \tconst char *cmdname;\n+\tstruct string_list_item *item;\n \n \tswitch (which_command) {\n \tcase CHECKOUT_CHECKOUT:\n@@ -1360,15 +1363,19 @@ static void advise_disambiguating_remotes(enum checkout_command which_command)\n \t\tbreak;\n \t}\n \n-\tadvise(_(\"If you meant to check out a remote tracking branch on, e.g. 'origin',\\n\"\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to check out a remote tracking branch on <remote>,\\n\"\n \t\t \"you can do so by fully qualifying the name with the --track option:\\n\"\n \t\t \"\\n\"\n-\t\t \"    git %s --track origin/<name>\\n\"\n+\t\t \"    git %s --track <remote>/%s\\n\"\n \t\t \"\\n\"\n-\t\t \"If you'd like to always have checkouts of an ambiguous <name> prefer\\n\"\n+\t\t \"If you'd like to always have checkouts of an ambiguous name prefer\\n\"\n \t\t \"one remote, e.g. the 'origin' remote, consider setting\\n\"\n \t\t \"checkout.defaultRemote=origin in your config.\"),\n-\t       cmdname);\n+\t       cmdname, branch);\n }\n \n static char *parse_remote_branch(const char *arg,\n@@ -1377,7 +1384,10 @@ static char *parse_remote_branch(const char *arg,\n \t\t\t\t enum checkout_command which_command)\n {\n \tint num_matches = 0;\n-\tchar *remote = unique_tracking_name(arg, rev, &num_matches);\n+\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\tchar *remote = unique_tracking_name(arg, rev, &num_matches,\n+\t\t\t\t\t    &matched_remote_names);\n \n \tif (remote && could_be_checkout_paths) {\n \t\tdie(_(\"'%s' could be both a local file and a tracking branch.\\n\"\n@@ -1387,12 +1397,14 @@ static char *parse_remote_branch(const char *arg,\n \n \tif (!remote && num_matches > 1) {\n \t\tif (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n-\t\t\tadvise_disambiguating_remotes(which_command);\n-\n+\t\t\tadvise_disambiguating_remotes(which_command, arg,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n \t\t    arg, num_matches);\n \t}\n \n+\tstring_list_clear(&matched_remote_names, 0);\n+\n \treturn remote;\n }\n \ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 654d27c3e1..22c8e5e131 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -782,7 +782,7 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL);\n+\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -904,7 +904,7 @@ static int add(int ac, const char **av, const char *prefix,\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL);\n+\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\ndiff --git a/checkout.c b/checkout.c\nindex 1588b116ee..a0d0229435 100644\n--- a/checkout.c\n+++ b/checkout.c\n@@ -8,6 +8,7 @@\n #include \"checkout.h\"\n #include \"config.h\"\n #include \"strbuf.h\"\n+#include \"string-list.h\"\n \n struct tracking_name_data {\n \t/* const */ char *src_ref;\n@@ -17,6 +18,7 @@ struct tracking_name_data {\n \tconst char *default_remote;\n \tchar *default_dst_ref;\n \tstruct object_id *default_dst_oid;\n+\tstruct string_list *remote_names;\n };\n \n #define TRACKING_NAME_DATA_INIT { 0 }\n@@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n \t\toidcpy(dst, cb->dst_oid);\n \t\tcb->default_dst_oid = dst;\n \t}\n+\tif (cb->remote_names)\n+\t\tstring_list_append(cb->remote_names, remote->name);\n \tif (cb->dst_ref) {\n \t\tfree(query.dst);\n \t\treturn 0;\n@@ -48,14 +52,19 @@ static int check_tracking_name(struct remote *remote, void *cb_data)\n }\n \n char *unique_tracking_name(const char *name, struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched)\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names)\n {\n \tstruct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;\n \tconst char *default_remote = NULL;\n-\tif (!repo_config_get_string_tmp(the_repository, \"checkout.defaultremote\", &default_remote))\n+\n+\tif (!repo_config_get_string_tmp(the_repository,\n+\t\t\t\t\t\"checkout.defaultremote\",\n+\t\t\t\t\t&default_remote))\n \t\tcb_data.default_remote = default_remote;\n \tcb_data.src_ref = xstrfmt(\"refs/heads/%s\", name);\n \tcb_data.dst_oid = oid;\n+\tcb_data.remote_names = dwim_remote_names;\n \tfor_each_remote(check_tracking_name, &cb_data);\n \tif (dwim_remotes_matched)\n \t\t*dwim_remotes_matched = cb_data.num_matches;\ndiff --git a/checkout.h b/checkout.h\nindex 55920e7aeb..0b185a0fc9 100644\n--- a/checkout.h\n+++ b/checkout.h\n@@ -3,6 +3,8 @@\n \n #include \"hash.h\"\n \n+struct string_list;\n+\n /*\n  * Check if the branch name uniquely matches a branch name on a remote\n  * tracking branch.  Return the name of the remote if such a branch\n@@ -10,6 +12,7 @@\n  */\n char *unique_tracking_name(const char *name,\n \t\t\t   struct object_id *oid,\n-\t\t\t   int *dwim_remotes_matched);\n+\t\t\t   int *dwim_remotes_matched,\n+\t\t\t   struct string_list *dwim_remote_names);\n \n #endif /* CHECKOUT_H */\n-- \ngitgitgadget\n\n"},{"id":"551364","messageId":"35814b47a49d7e6c0b3f19e8bf179f0bc7be2117.1787841717.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v10.git.1787841717.gitgitgadget@gmail.com","subject":"[PATCH v10 3/4] worktree add: improve message for ambiguous remote branch name","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-27T14:41:56Z","receivedAt":"2026-08-27T14:42:04Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen the user runs 'git worktree add ../foo-dir bar-topic' without\nspecifying a remote, and there is no local branch named bar-topic, we\ntry to guess which remote branch bar-topic refers to, then create a\nnew branch named bar-topic that tracks the remote branch.\n\nIf multiple remotes have a branch named bar-topic, we silently gave\nup, leaving the variable 'branch' intact.  We then entered the\nconditional clause 'if (!opts.orphan &&\n!lookup_commit_reference_by_name(branch))' and triggered an \"invalid\nreference\" error.  This error message did not provide enough\ninformation to resolve the ambiguity.\n\nWhen multiple matching branches are found, display a hint and a\ndescriptive error message and die immediately.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n builtin/worktree.c      | 37 ++++++++++++++++++++++++++++++++++---\n t/t2400-worktree-add.sh |  4 ++--\n 2 files changed, 36 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 22c8e5e131..c745deddde 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -764,6 +764,25 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)\n \treturn 1;\n }\n \n+static void advise_disambiguating_remotes(const char *path, const char *branch,\n+\t\t\t\t\t  const struct string_list *matched_remote_names)\n+{\n+\tstruct string_list_item *item;\n+\n+\tadvise(_(\"Branch name '%s' appears in multiple remotes:\"), branch);\n+\tfor_each_string_list_item(item, matched_remote_names) {\n+\t\tadvise(_(\"  %s\"), item->string);\n+\t}\n+\tadvise(_(\"If you meant to create a worktree from a remote tracking branch on\\n\"\n+\t\t \"<remote>, you can do so by:\\n\"\n+\t\t \"\\n\"\n+\t\t \"    git worktree add -b %s %s <remote>/%s\\n\"\n+\t\t \"\\n\"\n+\t\t \"If you'd like to always prefer some remote, e.g. 'origin',\\n\"\n+\t\t \"consider setting checkout.defaultRemote=origin in your config.\"),\n+\t       branch, path, branch);\n+}\n+\n static char *dwim_branch(const char *path, char **new_branch)\n {\n \tint n;\n@@ -898,17 +917,29 @@ static int add(int ac, const char **av, const char *prefix,\n \t\t/* DWIM: Infer --orphan when repo has no refs. */\n \t\topts.orphan = (!s) && dwim_orphan(&opts, !!opt_track, 1);\n \t} else if (ac == 2) {\n-\t\tstruct object_id oid;\n \t\tstruct commit *commit;\n-\t\tchar *remote;\n \n \t\tcommit = lookup_commit_reference_by_name(branch);\n \t\tif (!commit) {\n-\t\t\tremote = unique_tracking_name(branch, &oid, NULL, NULL);\n+\t\t\tstruct object_id oid;\n+\t\t\tchar *remote;\n+\t\t\tint num_matches = 0;\n+\t\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\t\tremote = unique_tracking_name(branch, &oid, &num_matches,\n+\t\t\t\t\t\t      &matched_remote_names);\n \t\t\tif (remote) {\n \t\t\t\tnew_branch = branch;\n \t\t\t\tbranch = new_branch_to_free = remote;\n+\t\t\t} else if (num_matches > 1) {\n+\t\t\t\tif (!opts.quiet &&\n+\t\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\t\tadvise_disambiguating_remotes(path, branch,\n+\t\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t\t    branch, num_matches);\n \t\t\t}\n+\t\t\tstring_list_clear(&matched_remote_names, 0);\n \t\t}\n \n \t\tif (!strcmp(branch, \"HEAD\"))\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 87b926728a..5c105cf252 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -624,12 +624,12 @@ test_expect_success '\"add\" <path> <branch> dwims' '\n test_expect_success '\"add\" <path> <branch> dwims with checkout.defaultRemote' '\n \ttest_when_finished rm -rf repo_upstream repo_dwim foo &&\n \tsetup_remote_repo repo_upstream repo_dwim &&\n-\tgit init repo_dwim &&\n \t(\n \t\tcd repo_dwim &&\n \t\tgit remote add repo_upstream2 ../repo_upstream &&\n \t\tgit fetch repo_upstream2 &&\n-\t\ttest_must_fail git worktree add ../foo foo &&\n+\t\ttest_must_fail git worktree add ../foo foo 2>error.actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" error.actual &&\n \t\tgit -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&\n \t\tgit status -uno --porcelain >status.actual &&\n \t\ttest_must_be_empty status.actual\n-- \ngitgitgadget\n\n"},{"id":"551365","messageId":"407c53b33c1b4fb2b1919b4552cf15a71deb7cb8.1787841717.git.gitgitgadget@gmail.com","threadId":"66141","inReplyTo":"pull.2197.v10.git.1787841717.gitgitgadget@gmail.com","subject":"[PATCH v10 4/4] worktree add: treat multiple matches with --guess-remote as an error","fromName":"Yoichi NAKAYAMA via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-08-27T14:41:57Z","receivedAt":"2026-08-27T14:42:04Z","isPatch":true,"body":"From: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n\nWhen 'git worktree add <path>' is invoked without <commit-ish> and\nwith the --guess-remote option (or when worktree.guessRemote is set to\ntrue), it tries to find a remote-tracking branch matching the basename\nof <path>.\n\nCurrently, the behavior when multiple matches are found is the same as\nwhen no match is found: it falls back to creating a branch from\nHEAD. This has been the behavior since 71d6682d8c (worktree: add\n--guess-remote option to add subcommand, 2017-11-29), when the option\nwas first introduced.\n\nHowever, if the specified <path> matches any remote-tracking branch,\nwe infer that the user intended to use one of the remote-tracking\nbranches as the start-point rather than HEAD. So we abort the creation\nof the branch and worktree when there are multiple matches, and\ninstruct the user to choose the start-point.\n\nSigned-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>\n---\n Documentation/config/worktree.adoc |  5 +++--\n Documentation/git-worktree.adoc    |  4 +++-\n builtin/worktree.c                 | 20 +++++++++++++++++---\n t/t2400-worktree-add.sh            | 13 +++++++++++++\n 4 files changed, 36 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/worktree.adoc b/Documentation/config/worktree.adoc\nindex a248076ea5..0930183b91 100644\n--- a/Documentation/config/worktree.adoc\n+++ b/Documentation/config/worktree.adoc\n@@ -5,8 +5,9 @@\n \tset to true, `worktree add` tries to find a remote-tracking\n \tbranch whose name uniquely matches the new branch name.  If\n \tsuch a branch exists, it is checked out and set as \"upstream\"\n-\tfor the new branch.  If no such match can be found, it falls\n-\tback to creating a new branch from the current `HEAD`.\n+\tfor the new branch.  If multiple matches are found, the command\n+\tfails.  If no such match can be found, it falls back to\n+\tcreating a new branch from the current `HEAD`.\n \n `worktree.useRelativePaths`::\n \tLink worktrees using relative paths (when \"`true`\") or absolute\ndiff --git a/Documentation/git-worktree.adoc b/Documentation/git-worktree.adoc\nindex fbf8426cd9..32787eacc3 100644\n--- a/Documentation/git-worktree.adoc\n+++ b/Documentation/git-worktree.adoc\n@@ -219,7 +219,9 @@ To remove a locked worktree, specify `--force` twice.\n \tof creating a new branch from `HEAD`, if there exists a tracking\n \tbranch in exactly one remote matching the basename of _<path>_,\n \tbase the new branch on the remote-tracking branch, and mark\n-\tthe remote-tracking branch as \"upstream\" from the new branch.\n+\tthe remote-tracking branch as \"upstream\" from the new branch. If\n+\tthere are multiple matches, the command fails. If there is no\n+\tmatch, the command falls back to creating a new branch from `HEAD`.\n +\n This can also be set up as the default behaviour by using the\n `worktree.guessRemote` config option.\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex c745deddde..07163bf9b7 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -783,7 +783,7 @@ static void advise_disambiguating_remotes(const char *path, const char *branch,\n \t       branch, path, branch);\n }\n \n-static char *dwim_branch(const char *path, char **new_branch)\n+static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)\n {\n \tint n;\n \tint branch_exists;\n@@ -801,7 +801,21 @@ static char *dwim_branch(const char *path, char **new_branch)\n \t*new_branch = branchname;\n \tif (guess_remote) {\n \t\tstruct object_id oid;\n-\t\tchar *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);\n+\t\tchar *remote;\n+\t\tint num_matches = 0;\n+\t\tstruct string_list matched_remote_names = STRING_LIST_INIT_DUP;\n+\n+\t\tremote = unique_tracking_name(*new_branch, &oid, &num_matches,\n+\t\t\t\t\t      &matched_remote_names);\n+\t\tif (!remote && num_matches > 1) {\n+\t\t\tif (!opts->quiet &&\n+\t\t\t    advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))\n+\t\t\t\tadvise_disambiguating_remotes(path, *new_branch,\n+\t\t\t\t\t\t\t      &matched_remote_names);\n+\t\t\tdie(_(\"'%s' matched multiple (%d) remote tracking branches\"),\n+\t\t\t    *new_branch, num_matches);\n+\t\t}\n+\t\tstring_list_clear(&matched_remote_names, 0);\n \t\treturn remote;\n \t}\n \treturn NULL;\n@@ -909,7 +923,7 @@ static int add(int ac, const char **av, const char *prefix,\n \t\topts.orphan = dwim_orphan(&opts, !!opt_track, 0);\n \t} else if (ac < 2) {\n \t\t/* DWIM: Guess branch name from path. */\n-\t\tchar *s = dwim_branch(path, &new_branch_to_free);\n+\t\tchar *s = dwim_branch(&opts, path, &new_branch_to_free);\n \t\tif (s)\n \t\t\tbranch = branch_to_free = s;\n \t\tnew_branch = new_branch_to_free;\ndiff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh\nindex 5c105cf252..a37137042d 100755\n--- a/t/t2400-worktree-add.sh\n+++ b/t/t2400-worktree-add.sh\n@@ -669,6 +669,19 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '\n \t\ttest_cmp_rev refs/remotes/repo_a/foo refs/heads/foo\n \t)\n '\n+\n+test_expect_success 'git worktree add --guess-remote fails if there are multiple matches' '\n+\ttest_when_finished rm -rf repo_a repo_b foo &&\n+\tsetup_remote_repo repo_a repo_b &&\n+\t(\n+\t\tcd repo_b &&\n+\t\tgit remote add repo_a2 ../repo_a &&\n+\t\tgit fetch repo_a2 &&\n+\t\ttest_must_fail git worktree add --guess-remote ../foo 2>actual &&\n+\t\ttest_grep \"matched multiple (2) remote tracking branches\" actual\n+\t)\n+'\n+\n test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '\n \ttest_when_finished rm -rf repo_a repo_b foo &&\n \tsetup_remote_repo repo_a repo_b &&\n-- \ngitgitgadget\n"},{"id":"551381","messageId":"xmqq4igfa2pv.fsf@gitster.g","threadId":"66141","inReplyTo":"pull.2197.v10.git.1787841717.gitgitgadget@gmail.com","subject":"Re: [PATCH v10 0/4] worktree add: improve message for ambiguous remote branch name","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-08-27T17:32:44Z","receivedAt":"2026-08-27T17:32:46Z","isPatch":true,"body":"\"Yoichi NAKAYAMA via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> 'git worktree add ../foo-dir bar-topic' fails to dwim when there are\n> multiple remote branches with name `bar-topic'. But it doesn't display\n> meaningful message as 'git checkout bar-topic' does under the same\n> situation.\n>\n> We improve this by adding advice and modify the error message for worktree\n> add.\n>\n> By Junio's suggestion, we include matched remote names in the advice. It is\n> applied to checkout, too.\n>\n> We also fix the behavior of --guess-remote when there are multiple matches.\n>\n> Changes from the previous patch:\n>\n>  * [1/4] fix the function name and add detailed commit message\n>  * [2/4] change type of tracking_name_data.remote_names\n\nThis iteration looks good to me.  Let's mark it for 'next' unless\nwe hear other comments in a few days.\n\nThanks.\n"}]}