Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d0ee845
builtin/add.c: replace run_command() with direct apply_all_patches() …
Jul 11, 2026
da9c6e7
completion: zsh: support completion after "git -C <path>"
mobilutz Aug 19, 2026
aae63be
reftable/stack: remove `REFTABLE_STACK_NEW_ADDITION_RELOAD`
KarthikNayak Aug 24, 2026
cbd35ca
reftable/stack: rename reftable_stack_new_addition()
KarthikNayak Aug 24, 2026
654567c
reftable/stack: move list lock to `struct reftable_stack`
KarthikNayak Aug 24, 2026
976644c
reftable/stack: avoid reloading the stack when already locked
KarthikNayak Aug 24, 2026
1af4c26
checkout: extract function to display advice for ambiguous remotes
yoichi Aug 27, 2026
05cf4ce
checkout: improve message for ambiguous remote branch name
yoichi Aug 27, 2026
8548960
worktree add: improve message for ambiguous remote branch name
yoichi Aug 27, 2026
b70101c
worktree add: treat multiple matches with --guess-remote as an error
yoichi Aug 27, 2026
8ace322
you_still_use_that(): reword the instructions
gitster Aug 27, 2026
53d330c
worktree repair: detect relative path in .git file correctly
yoichi Aug 28, 2026
9ef0ca0
Merge branch 'kn/reftable-optimize-reloading'
gitster Sep 7, 2026
bf7d128
Merge branch 'yn/worktree-ambiguous-remote-advice'
gitster Sep 7, 2026
7c5e5d7
Merge branch 'jc/you-still-use-that'
gitster Sep 7, 2026
8043d64
Merge branch 'gr/add-e-use-apply-api'
gitster Sep 7, 2026
098d380
Merge branch 'll/zsh-complete-git-potty-options'
gitster Sep 7, 2026
d595003
Merge branch 'yn/worktree-repair-relative'
gitster Sep 7, 2026
b8242b0
The 23rd batch
gitster Sep 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Documentation/RelNotes/2.56.0.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ UI, Workflows & Features
* The command line completion (in contrib/) has been taught to handle
the experimental 'git history' command.

* 'git checkout' and 'git worktree add' makes guesses based on a name
of a remote-tracking branch, but does not give an error when such a
remote-tracking branch cannot be uniquely identified, which has
been corrected.


Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
Expand Down Expand Up @@ -484,6 +489,15 @@ Performance, Internal Implementation, Development Support etc.
'struct repository', continuing the libification process and
allowing per-repository control (such as for submodules).

* The reftable code has been optimized to avoid an unnecessary
stat/reload of the stack when an addition already holds the
list_file lock, reducing the number of newfstatat syscalls from
linear to constant when writing refs.

* The application of the edited patch in 'git add -e' has been
refactored to use the internal apply API directly, avoiding the need
to spawn a 'git apply' subprocess.


Fixes since v2.55
-----------------
Expand Down Expand Up @@ -748,6 +762,23 @@ Fixes since v2.55
This prevents intended textual URLs from being mangled or mistakenly
treated as metadata keys.

* The instructions for deprecated commands emitted by
you_still_use_that() have been reworded to clarify that the removal
decision is final and to provide more assertive guidance on finding
a replacement.
(merge 8ace32221c jc/you-still-use-that later to maint).

* The zsh completion script (in 'contrib/') has been updated to
correctly locate the Git command after global options like '-C' by
properly skipping them, similar to how the bash completion does.

* The git worktree repair command failed to rewrite the .git file of
a working tree from a relative path to an absolute path when the
command was run in the working tree itself. The
read_gitfile_gently() function was modified to also return whether
the path originally recorded in the file was absolute, and this new
capability is used to correctly detect such mismatches.

* Other code cleanup, docfix, build fix, etc.
(merge 026636128f ss/submittingpatches-typofix later to maint).
(merge d2af22cc21 jc/rerere-doc-typofix later to maint).
5 changes: 3 additions & 2 deletions Documentation/config/worktree.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion Documentation/git-worktree.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
19 changes: 12 additions & 7 deletions builtin/add.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
#include "dir.h"
#include "gettext.h"
#include "pathspec.h"
#include "run-command.h"
#include "object-file.h"
#include "odb.h"
#include "odb/transaction.h"
Expand All @@ -23,10 +22,10 @@
#include "diff.h"
#include "read-cache.h"
#include "revision.h"
#include "strvec.h"
#include "submodule.h"
#include "add-interactive.h"
#include "merge-ll.h"
#include "apply.h"

static const char * const builtin_add_usage[] = {
N_("git add [<options>] [--] <pathspec>..."),
Expand Down Expand Up @@ -189,7 +188,8 @@ static int edit_patch(struct repository *repo,
const char *prefix)
{
char *file = repo_git_path(repo, "ADD_EDIT.patch");
struct child_process child = CHILD_PROCESS_INIT;
struct apply_state state;
const char *apply_argv[2];
struct rev_info rev;
int out;
struct stat st;
Expand Down Expand Up @@ -219,11 +219,16 @@ static int edit_patch(struct repository *repo,
if (!st.st_size)
die(_("empty patch. aborted"));

child.git_cmd = 1;
strvec_pushl(&child.args, "apply", "--recount", "--cached", file,
NULL);
if (run_command(&child))
apply_argv[0] = file;
apply_argv[1] = NULL;
if (init_apply_state(&state, repo, NULL))
die(_("could not initialize apply state"));
state.cached = 1;
if (check_apply_state(&state, 0))
die(_("could not check apply state"));
if (apply_all_patches(&state, 1, apply_argv, APPLY_OPT_RECOUNT))
die(_("could not apply '%s'"), file);
clear_apply_state(&state);

unlink(file);
free(file);
Expand Down
76 changes: 46 additions & 30 deletions builtin/checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -1343,13 +1343,51 @@ enum checkout_command {
CHECKOUT_RESTORE = 3,
};

static void advise_disambiguating_remotes(enum checkout_command which_command,
const char *branch,
const struct string_list *matched_remote_names)
{
const char *cmdname;
struct string_list_item *item;

switch (which_command) {
case CHECKOUT_CHECKOUT:
cmdname = "checkout";
break;
case CHECKOUT_SWITCH:
cmdname = "switch";
break;
default:
BUG("command <%d> should not reach advise_disambiguating_remotes",
which_command);
break;
}

advise(_("Branch name '%s' appears in multiple remotes:"), branch);
for_each_string_list_item(item, matched_remote_names) {
advise(_(" %s"), item->string);
}
advise(_("If you meant to check out a remote tracking branch on <remote>,\n"
"you can do so by fully qualifying the name with the --track option:\n"
"\n"
" git %s --track <remote>/%s\n"
"\n"
"If you'd like to always have checkouts of an ambiguous name prefer\n"
"one remote, e.g. the 'origin' remote, consider setting\n"
"checkout.defaultRemote=origin in your config."),
cmdname, branch);
}

static char *parse_remote_branch(const char *arg,
struct object_id *rev,
int could_be_checkout_paths,
enum checkout_command which_command)
{
int num_matches = 0;
char *remote = unique_tracking_name(arg, rev, &num_matches);
struct string_list matched_remote_names = STRING_LIST_INIT_DUP;

char *remote = unique_tracking_name(arg, rev, &num_matches,
&matched_remote_names);

if (remote && could_be_checkout_paths) {
die(_("'%s' could be both a local file and a tracking branch.\n"
Expand All @@ -1358,37 +1396,15 @@ static char *parse_remote_branch(const char *arg,
}

if (!remote && num_matches > 1) {
if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME)) {
const char *cmdname;

switch (which_command) {
case CHECKOUT_CHECKOUT:
cmdname = "checkout";
break;
case CHECKOUT_SWITCH:
cmdname = "switch";
break;
default:
BUG("command <%d> should not reach parse_remote_branch",
which_command);
break;
}

advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
"you can do so by fully qualifying the name with the --track option:\n"
"\n"
" git %s --track origin/<name>\n"
"\n"
"If you'd like to always have checkouts of an ambiguous <name> prefer\n"
"one remote, e.g. the 'origin' remote, consider setting\n"
"checkout.defaultRemote=origin in your config."),
cmdname);
}

die(_("'%s' matched multiple (%d) remote tracking branches"),
arg, num_matches);
if (advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))
advise_disambiguating_remotes(which_command, arg,
&matched_remote_names);
die(_("'%s' matched multiple (%d) remote tracking branches"),
arg, num_matches);
}

string_list_clear(&matched_remote_names, 0);

return remote;
}

Expand Down
57 changes: 51 additions & 6 deletions builtin/worktree.c
Original file line number Diff line number Diff line change
Expand Up @@ -764,7 +764,26 @@ static int dwim_orphan(const struct add_opts *opts, int opt_track, int remote)
return 1;
}

static char *dwim_branch(const char *path, char **new_branch)
static void advise_disambiguating_remotes(const char *path, const char *branch,
const struct string_list *matched_remote_names)
{
struct string_list_item *item;

advise(_("Branch name '%s' appears in multiple remotes:"), branch);
for_each_string_list_item(item, matched_remote_names) {
advise(_(" %s"), item->string);
}
advise(_("If you meant to create a worktree from a remote tracking branch on\n"
"<remote>, you can do so by:\n"
"\n"
" git worktree add -b %s %s <remote>/%s\n"
"\n"
"If you'd like to always prefer some remote, e.g. 'origin',\n"
"consider setting checkout.defaultRemote=origin in your config."),
branch, path, branch);
}

static char *dwim_branch(const struct add_opts *opts, const char *path, char **new_branch)
{
int n;
int branch_exists;
Expand All @@ -782,7 +801,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);
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;
Expand Down Expand Up @@ -890,7 +923,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;
Expand All @@ -901,17 +934,29 @@ static int add(int ac, const char **av, const char *prefix,
if (!strcmp(branch, "HEAD"))
can_use_local_refs(&opts);
} else if (ac == 2) {
struct object_id oid;
struct commit *commit;
char *remote;

commit = lookup_commit_reference_by_name(branch);
if (!commit) {
remote = unique_tracking_name(branch, &oid, NULL);
struct object_id oid;
char *remote;
int num_matches = 0;
struct string_list matched_remote_names = STRING_LIST_INIT_DUP;

remote = unique_tracking_name(branch, &oid, &num_matches,
&matched_remote_names);
if (remote) {
new_branch = branch;
branch = new_branch_to_free = remote;
} else if (num_matches > 1) {
if (!opts.quiet &&
advice_enabled(ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME))
advise_disambiguating_remotes(path, branch,
&matched_remote_names);
die(_("'%s' matched multiple (%d) remote tracking branches"),
branch, num_matches);
}
string_list_clear(&matched_remote_names, 0);
}

if (!strcmp(branch, "HEAD"))
Expand Down
13 changes: 11 additions & 2 deletions checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "checkout.h"
#include "config.h"
#include "strbuf.h"
#include "string-list.h"

struct tracking_name_data {
/* const */ char *src_ref;
Expand All @@ -17,6 +18,7 @@ struct tracking_name_data {
const char *default_remote;
char *default_dst_ref;
struct object_id *default_dst_oid;
struct string_list *remote_names;
};

#define TRACKING_NAME_DATA_INIT { 0 }
Expand All @@ -39,6 +41,8 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
oidcpy(dst, cb->dst_oid);
cb->default_dst_oid = dst;
}
if (cb->remote_names)
string_list_append(cb->remote_names, remote->name);
if (cb->dst_ref) {
free(query.dst);
return 0;
Expand All @@ -48,14 +52,19 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
}

char *unique_tracking_name(const char *name, struct object_id *oid,
int *dwim_remotes_matched)
int *dwim_remotes_matched,
struct string_list *dwim_remote_names)
{
struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
const char *default_remote = NULL;
if (!repo_config_get_string_tmp(the_repository, "checkout.defaultremote", &default_remote))

if (!repo_config_get_string_tmp(the_repository,
"checkout.defaultremote",
&default_remote))
cb_data.default_remote = default_remote;
cb_data.src_ref = xstrfmt("refs/heads/%s", name);
cb_data.dst_oid = oid;
cb_data.remote_names = dwim_remote_names;
for_each_remote(check_tracking_name, &cb_data);
if (dwim_remotes_matched)
*dwim_remotes_matched = cb_data.num_matches;
Expand Down
5 changes: 4 additions & 1 deletion checkout.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@

#include "hash.h"

struct string_list;

/*
* Check if the branch name uniquely matches a branch name on a remote
* tracking branch. Return the name of the remote if such a branch
* exists, NULL otherwise.
*/
char *unique_tracking_name(const char *name,
struct object_id *oid,
int *dwim_remotes_matched);
int *dwim_remotes_matched,
struct string_list *dwim_remote_names);

#endif /* CHECKOUT_H */
Loading