worktree add: treat multiple matches with --guess-remote as an error

When 'git worktree add <path>' is invoked without <commit-ish> and
with the --guess-remote option (or when worktree.guessRemote is set to
true), it tries to find a remote-tracking branch matching the basename
of <path>.

Currently, the behavior when multiple matches are found is the same as
when no match is found: it falls back to creating a branch from
HEAD. This has been the behavior since 71d6682d8c (worktree: add
--guess-remote option to add subcommand, 2017-11-29), when the option
was first introduced.

However, if the specified <path> matches any remote-tracking branch,
we infer that the user intended to use one of the remote-tracking
branches as the start-point rather than HEAD. So we abort the creation
of the branch and worktree when there are multiple matches, and
instruct the user to choose the start-point.

Signed-off-by: Yoichi NAKAYAMA <yoichi.nakayama@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
next
Yoichi NAKAYAMA 2026-08-27 14:41:57 +00:00 committed by Junio C Hamano
parent 85489606d8
commit b70101c224
4 changed files with 36 additions and 6 deletions

View File

@ -5,8 +5,9 @@
set to true, `worktree add` tries to find a remote-tracking
branch whose name uniquely matches the new branch name. If
such a branch exists, it is checked out and set as "upstream"
for the new branch. If no such match can be found, it falls
back to creating a new branch from the current `HEAD`.
for the new branch. If multiple matches are found, the command
fails. If no such match can be found, it falls back to
creating a new branch from the current `HEAD`.

`worktree.useRelativePaths`::
Link worktrees using relative paths (when "`true`") or absolute

View File

@ -219,7 +219,9 @@ To remove a locked worktree, specify `--force` twice.
of creating a new branch from `HEAD`, if there exists a tracking
branch in exactly one remote matching the basename of _<path>_,
base the new branch on the remote-tracking branch, and mark
the remote-tracking branch as "upstream" from the new branch.
the remote-tracking branch as "upstream" from the new branch. If
there are multiple matches, the command fails. If there is no
match, the command falls back to creating a new branch from `HEAD`.
+
This can also be set up as the default behaviour by using the
`worktree.guessRemote` config option.

View File

@ -782,7 +782,7 @@ static void advise_disambiguating_remotes(const char *path, const char *branch,
branch, path, branch);
}

static char *dwim_branch(const char *path, char **new_branch)
static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)
{
int n;
int branch_exists;
@ -800,7 +800,21 @@ static char *dwim_branch(const char *path, char **new_branch)
*new_branch = branchname;
if (guess_remote) {
struct object_id oid;
char *remote = unique_tracking_name(*new_branch, &oid, NULL, NULL);
char *remote;
int num_matches = 0;
struct string_list matched_remote_names = STRING_LIST_INIT_DUP;

remote = unique_tracking_name(*new_branch, &oid, &num_matches,
&matched_remote_names);
if (!remote && num_matches > 1) {
if (!opts->quiet &&
advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))
advise_disambiguating_remotes(path, *new_branch,
&matched_remote_names);
die(_("'%s' matched multiple (%d) remote tracking branches"),
*new_branch, num_matches);
}
string_list_clear(&matched_remote_names, 0);
return remote;
}
return NULL;
@ -908,7 +922,7 @@ static int add(int ac, const char **av, const char *prefix,
opts.orphan = dwim_orphan(&opts, !!opt_track, 0);
} else if (ac < 2) {
/* DWIM: Guess branch name from path. */
char *s = dwim_branch(path, &new_branch_to_free);
char *s = dwim_branch(&opts, path, &new_branch_to_free);
if (s)
branch = branch_to_free = s;
new_branch = new_branch_to_free;

View File

@ -669,6 +669,19 @@ test_expect_success 'git worktree add --guess-remote sets up tracking' '
test_cmp_rev refs/remotes/repo_a/foo refs/heads/foo
)
'

test_expect_success 'git worktree add --guess-remote fails if there are multiple matches' '
test_when_finished rm -rf repo_a repo_b foo &&
setup_remote_repo repo_a repo_b &&
(
cd repo_b &&
git remote add repo_a2 ../repo_a &&
git fetch repo_a2 &&
test_must_fail git worktree add --guess-remote ../foo 2>actual &&
test_grep "matched multiple (2) remote tracking branches" actual
)
'

test_expect_success 'git worktree add --guess-remote sets up tracking (quiet)' '
test_when_finished rm -rf repo_a repo_b foo &&
setup_remote_repo repo_a repo_b &&