Git development
 help / color / mirror / Atom feed
* [PATCH v10] checkout: extend --track with a "fetch" mode to refresh start-point
From: Harald Nordgren via GitGitGadget @ 2026-05-18  8:04 UTC (permalink / raw)
  To: git
  Cc: Ramsay Jones, D. Ben Knoble, Kristoffer Haugsbakk, Marc Branchaud,
	Phillip Wood, Harald Nordgren, Harald Nordgren
In-Reply-To: <pull.2281.v9.git.git.1778583307774.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

If you want to fork your topic branch from the very latest of the
tip of a branch your remote has, you would do:

    git fetch origin some-branch
    git checkout -b new_branch --track origin/some-branch

Extend the "--track" option of "git checkout" and allow users to
write

    git checkout -b new_branch --track=fetch origin/some-branch

to (1) fetch 'some-branch' from the remote 'origin', updating the
remote-tracking branch 'origin/some-branch', (2) arrange subsequent
'git pull' on 'new_branch' to interact with 'origin/some-branch' and
(3) fork 'new_branch' from it.

In the value of the '--track' option, 'fetch' can be combined with
the existing 'direct' (default) and 'inherit' modes via a
comma-separated list. Examples:

    git checkout -b new_branch --track=fetch,inherit some_local_branch
    git switch -c new_branch --track=fetch origin/some-branch

When "fetch" is requested and <start-point> is in <remote>/<branch>
form, run "git fetch <remote> <branch>" before resolving the ref, so
that other remote-tracking branches are left untouched. If
<start-point> is a bare remote name like "origin" (which resolves to
that remote's default branch), "git fetch <remote>" is run instead,
since the target branch is not known up front. Abort the checkout if
the fetch fails.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
    checkout: --track=fetch
    
    Rebased to fix merge conflict with master.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2281%2FHaraldNordgren%2Fcheckout-fetch-start-point-v10
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2281/HaraldNordgren/checkout-fetch-start-point-v10
Pull-Request: https://github.com/git/git/pull/2281

Range-diff vs v9:

 1:  021375e4cc ! 1:  a773fb6bdf checkout: extend --track with a "fetch" mode to refresh start-point
     @@ Documentation/git-checkout.adoc: of it").
       the refspec configured for the corresponding remote, and then stripping
      
       ## Documentation/git-switch.adoc ##
     -@@ Documentation/git-switch.adoc: should result in deletion of the path).
     +@@ Documentation/git-switch.adoc: variable.
       	attached to a terminal, regardless of `--quiet`.
       
       `-t`::
     @@ builtin/checkout.c
       #include "resolve-undo.h"
       #include "revision.h"
      +#include "run-command.h"
     + #include "sequencer.h"
       #include "setup.h"
       #include "strvec.h"
     - #include "submodule.h"
      @@ builtin/checkout.c: struct checkout_opts {
       	int count_checkout_paths;
       	int overlay_mode;


 Documentation/git-checkout.adoc |  13 ++-
 Documentation/git-switch.adoc   |  13 ++-
 builtin/checkout.c              | 168 +++++++++++++++++++++++++++++++-
 t/t7201-co.sh                   | 144 +++++++++++++++++++++++++++
 4 files changed, 332 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc
index a8b3b8c2e2..ec63434159 100644
--- a/Documentation/git-checkout.adoc
+++ b/Documentation/git-checkout.adoc
@@ -158,11 +158,22 @@ of it").
 	resets _<branch>_ to the start point instead of failing.
 
 `-t`::
-`--track[=(direct|inherit)]`::
+`--track[=(direct|inherit|fetch)[,...]]`::
 	When creating a new branch, set up "upstream" configuration. See
 	`--track` in linkgit:git-branch[1] for details. As a convenience,
 	--track without -b implies branch creation.
 +
+The argument is a comma-separated list. `direct` (the default) and
+`inherit` select the tracking mode and are mutually exclusive. Adding
+`fetch` requests that the remote be fetched before _<start-point>_ is
+resolved, so the new branch starts from a fresh tip: when
+_<start-point>_ is in _<remote>/<branch>_ form, only that branch is
+updated; when _<start-point>_ is a bare remote name (e.g. `origin`),
+only the remote's default branch is updated. If the fetch fails and the
+corresponding remote-tracking ref already exists, a warning is printed
+and the checkout proceeds from the existing tip; otherwise the checkout
+is aborted.
++
 If no `-b` option is given, the name of the new branch will be
 derived from the remote-tracking branch, by looking at the local part of
 the refspec configured for the corresponding remote, and then stripping
diff --git a/Documentation/git-switch.adoc b/Documentation/git-switch.adoc
index d6c4f229a5..b5e79435cd 100644
--- a/Documentation/git-switch.adoc
+++ b/Documentation/git-switch.adoc
@@ -155,11 +155,22 @@ variable.
 	attached to a terminal, regardless of `--quiet`.
 
 `-t`::
-`--track[ (direct|inherit)]`::
+`--track[=(direct|inherit|fetch)[,...]]`::
 	When creating a new branch, set up "upstream" configuration.
 	`-c` is implied. See `--track` in linkgit:git-branch[1] for
 	details.
 +
+The argument is a comma-separated list. `direct` (the default) and
+`inherit` select the tracking mode and are mutually exclusive. Adding
+`fetch` requests that the remote be fetched before _<start-point>_ is
+resolved, so the new branch starts from a fresh tip: when
+_<start-point>_ is in _<remote>/<branch>_ form, only that branch is
+updated; when _<start-point>_ is a bare remote name (e.g. `origin`),
+only the remote's default branch is updated. If the fetch fails and the
+corresponding remote-tracking ref already exists, a warning is printed
+and the switch proceeds from the existing tip; otherwise the switch is
+aborted.
++
 If no `-c` option is given, the name of the new branch will be derived
 from the remote-tracking branch, by looking at the local part of the
 refspec configured for the corresponding remote, and then stripping
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 1345e8574a..fc58456546 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -25,10 +25,12 @@
 #include "preload-index.h"
 #include "read-cache.h"
 #include "refs.h"
+#include "refspec.h"
 #include "remote.h"
 #include "repo-settings.h"
 #include "resolve-undo.h"
 #include "revision.h"
+#include "run-command.h"
 #include "sequencer.h"
 #include "setup.h"
 #include "strvec.h"
@@ -62,6 +64,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int dwim_new_local_branch;
+	int fetch;
 	int discard_changes;
 	int accept_ref;
 	int accept_pathspec;
@@ -115,6 +118,158 @@ struct branch_info {
 	char *checkout;
 };
 
+static int resolve_fetch_target(const char *arg, char **remote_out,
+				char **src_ref_out, char **existing_ref_out)
+{
+	const char *slash;
+	char *remote_name = NULL;
+	struct remote *remote = NULL;
+	struct refspec_item query = { 0 };
+	struct strbuf dst = STRBUF_INIT;
+	struct object_id oid;
+	const char *rest = NULL;
+	const char *head_target = NULL;
+	const char *short_target;
+
+	*remote_out = NULL;
+	*src_ref_out = NULL;
+	*existing_ref_out = NULL;
+
+	if (!arg || !*arg || *arg == '/')
+		return -1;
+
+	slash = arg + strlen(arg);
+	while (1) {
+		free(remote_name);
+		remote_name = xstrndup(arg, slash - arg);
+		remote = remote_get(remote_name);
+		if (remote && remote_is_configured(remote, 1))
+			break;
+		while (slash > arg && *--slash != '/')
+			;
+		if (slash == arg) {
+			free(remote_name);
+			return -1;
+		}
+	}
+
+	if (*slash == '/' && slash[1])
+		rest = slash + 1;
+	if (!rest) {
+		strbuf_addf(&dst, "refs/remotes/%s/HEAD", remote_name);
+		head_target = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
+						      dst.buf,
+						      RESOLVE_REF_READING |
+						      RESOLVE_REF_NO_RECURSE,
+						      &oid, NULL);
+		if (head_target) {
+			*existing_ref_out = xstrdup(dst.buf);
+			if (skip_prefix(head_target, "refs/remotes/", &short_target) &&
+			    skip_prefix(short_target, remote_name, &short_target) &&
+			    *short_target == '/')
+				rest = short_target + 1;
+		}
+		strbuf_reset(&dst);
+	}
+
+	if (rest) {
+		strbuf_addf(&dst, "refs/remotes/%s/%s", remote_name, rest);
+		query.dst = dst.buf;
+		if (!remote_find_tracking(remote, &query) && query.src) {
+			*src_ref_out = xstrdup(query.src);
+			free(query.src);
+		} else {
+			*src_ref_out = xstrdup(rest);
+		}
+		if (!*existing_ref_out) {
+			strbuf_reset(&dst);
+			strbuf_addf(&dst, "refs/remotes/%s", arg);
+			if (!refs_read_ref(get_main_ref_store(the_repository),
+					   dst.buf, &oid))
+				*existing_ref_out = xstrdup(dst.buf);
+		}
+	}
+
+	strbuf_release(&dst);
+	*remote_out = remote_name;
+	return 0;
+}
+
+static void fetch_remote_for_start_point(const char *arg)
+{
+	char *remote_name = NULL;
+	char *src_ref = NULL;
+	char *existing_ref = NULL;
+	struct child_process cmd = CHILD_PROCESS_INIT;
+
+	if (resolve_fetch_target(arg, &remote_name, &src_ref, &existing_ref))
+		return;
+
+	strvec_pushl(&cmd.args, "fetch", remote_name, NULL);
+	if (src_ref)
+		strvec_push(&cmd.args, src_ref);
+	cmd.git_cmd = 1;
+	if (run_command(&cmd)) {
+		if (existing_ref)
+			warning(_("failed to fetch start-point '%s'; "
+				  "using existing '%s'"),
+				arg, existing_ref);
+		else
+			die(_("failed to fetch start-point '%s'"), arg);
+	}
+
+	free(remote_name);
+	free(src_ref);
+	free(existing_ref);
+}
+
+static int parse_opt_checkout_track(const struct option *opt,
+				    const char *arg, int unset)
+{
+	struct checkout_opts *opts = opt->value;
+	struct string_list tokens = STRING_LIST_INIT_DUP;
+	struct string_list_item *item;
+	int saw_direct = 0, saw_inherit = 0;
+	int ret = 0;
+
+	opts->fetch = 0;
+
+	if (unset) {
+		opts->track = BRANCH_TRACK_NEVER;
+		return 0;
+	}
+
+	opts->track = BRANCH_TRACK_EXPLICIT;
+	if (!arg)
+		return 0;
+
+	string_list_split(&tokens, arg, ",", -1);
+	for_each_string_list_item(item, &tokens) {
+		if (!strcmp(item->string, "fetch")) {
+			opts->fetch = 1;
+		} else if (!strcmp(item->string, "direct")) {
+			saw_direct = 1;
+			opts->track = BRANCH_TRACK_EXPLICIT;
+		} else if (!strcmp(item->string, "inherit")) {
+			saw_inherit = 1;
+			opts->track = BRANCH_TRACK_INHERIT;
+		} else {
+			ret = error(_("option `%s' expects \"%s\", \"%s\", "
+				      "or \"%s\""),
+				    "--track", "direct", "inherit", "fetch");
+			goto out;
+		}
+	}
+
+	if (saw_direct && saw_inherit)
+		ret = error(_("option `%s' cannot combine \"%s\" and \"%s\""),
+			    "--track", "direct", "inherit");
+
+out:
+	string_list_clear(&tokens, 0);
+	return ret;
+}
+
 static void branch_info_release(struct branch_info *info)
 {
 	free(info->name);
@@ -1733,10 +1888,10 @@ static struct option *add_common_switch_branch_options(
 {
 	struct option options[] = {
 		OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
-		OPT_CALLBACK_F('t', "track",  &opts->track, "(direct|inherit)",
+		OPT_CALLBACK_F('t', "track",  opts, "(direct|inherit|fetch)[,...]",
 			N_("set branch tracking configuration"),
 			PARSE_OPT_OPTARG,
-			parse_opt_tracking_mode),
+			parse_opt_checkout_track),
 		OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
 			   PARSE_OPT_NOCOMPLETE),
 		OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unborn branch")),
@@ -1941,8 +2096,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
-		int n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
-					     &new_branch_info, opts, &rev);
+		int n;
+
+		if (opts->fetch)
+			fetch_remote_for_start_point(argv[0]);
+
+		n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
+					 &new_branch_info, opts, &rev);
 		argv += n;
 		argc -= n;
 	} else if (!opts->accept_ref && opts->from_treeish) {
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 7613b1d2a4..75b46494c0 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -870,4 +870,148 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
 	test_cmp_config "" --default "" branch.main2.merge
 '
 
+test_expect_success 'setup upstream for --track=fetch tests' '
+	git checkout main &&
+	git init fetch_upstream &&
+	test_commit -C fetch_upstream u_main &&
+	git remote add fetch_upstream fetch_upstream &&
+	git fetch fetch_upstream &&
+	git -C fetch_upstream checkout -b fetch_new &&
+	test_commit -C fetch_upstream u_new
+'
+
+test_expect_success 'checkout --track=fetch -b picks up branch created upstream after clone' '
+	git checkout main &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_new &&
+	git checkout --track=fetch -b local_new fetch_upstream/fetch_new &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_new HEAD &&
+	test_cmp_config fetch_upstream branch.local_new.remote &&
+	test_cmp_config refs/heads/fetch_new branch.local_new.merge
+'
+
+test_expect_success 'checkout --track=fetch <remote>/<branch> leaves other tracking branches untouched' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_target &&
+	test_commit -C fetch_upstream u_target_pre &&
+	git -C fetch_upstream checkout -b fetch_other &&
+	test_commit -C fetch_upstream u_other_pre &&
+	git fetch fetch_upstream &&
+	other_before=$(git rev-parse refs/remotes/fetch_upstream/fetch_other) &&
+	git -C fetch_upstream checkout fetch_target &&
+	test_commit -C fetch_upstream u_target_post &&
+	git -C fetch_upstream checkout fetch_other &&
+	test_commit -C fetch_upstream u_other_post &&
+	git checkout --track=fetch -b local_target fetch_upstream/fetch_target &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_target HEAD &&
+	test "$(git rev-parse refs/remotes/fetch_upstream/fetch_other)" = "$other_before"
+'
+
+test_expect_success 'checkout --track=fetch with bare remote name fetches only <remote>/HEAD target' '
+	git checkout main &&
+	git -C fetch_upstream checkout main &&
+	git remote set-head fetch_upstream main &&
+	git -C fetch_upstream checkout -b fetch_unrelated &&
+	test_commit -C fetch_upstream u_unrelated_pre &&
+	git fetch fetch_upstream fetch_unrelated &&
+	unrelated_before=$(git rev-parse refs/remotes/fetch_upstream/fetch_unrelated) &&
+	git -C fetch_upstream checkout main &&
+	test_commit -C fetch_upstream u_main_post &&
+	git -C fetch_upstream checkout fetch_unrelated &&
+	test_commit -C fetch_upstream u_unrelated_post &&
+	git checkout --track=fetch -b local_from_remote fetch_upstream &&
+	test_cmp_rev refs/remotes/fetch_upstream/main HEAD &&
+	test "$(git rev-parse refs/remotes/fetch_upstream/fetch_unrelated)" = "$unrelated_before"
+'
+
+test_expect_success 'checkout --track=fetch aborts and does not create branch when no existing ref' '
+	git checkout main &&
+	test_might_fail git branch -D bogus &&
+	test_must_fail git checkout --track=fetch -b bogus fetch_upstream/does_not_exist &&
+	test_must_fail git rev-parse --verify refs/heads/bogus
+'
+
+test_expect_success 'checkout --track=fetch warns and proceeds when fetch fails but ref exists' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_offline &&
+	test_commit -C fetch_upstream u_offline &&
+	git fetch fetch_upstream fetch_offline &&
+	saved_url=$(git config remote.fetch_upstream.url) &&
+	test_when_finished "git config remote.fetch_upstream.url \"$saved_url\"" &&
+	git config remote.fetch_upstream.url ./does-not-exist &&
+	git checkout --track=fetch -b local_offline fetch_upstream/fetch_offline 2>err &&
+	test_grep "failed to fetch" err &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_offline HEAD
+'
+
+test_expect_success 'checkout --track=fetch resolves through configured fetch refspec' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_refspec &&
+	test_commit -C fetch_upstream u_refspec &&
+	git fetch fetch_upstream fetch_refspec &&
+	git remote add fetch_custom ./fetch_upstream &&
+	test_when_finished "git remote remove fetch_custom" &&
+	git config --replace-all remote.fetch_custom.fetch \
+		"+refs/heads/*:refs/remotes/custom-ns/*" &&
+	git fetch fetch_custom &&
+	test_commit -C fetch_upstream u_refspec_post &&
+	git checkout --track=fetch -b local_refspec custom-ns/fetch_refspec &&
+	test_cmp_rev refs/remotes/custom-ns/fetch_refspec HEAD
+'
+
+test_expect_success 'checkout --track=fetch handles hierarchical remote name' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_hier &&
+	test_commit -C fetch_upstream u_hier &&
+	git remote add nested/remote ./fetch_upstream &&
+	test_when_finished "git remote remove nested/remote" &&
+	git fetch nested/remote fetch_hier &&
+	test_commit -C fetch_upstream u_hier_post &&
+	git checkout --track=fetch -b local_hier nested/remote/fetch_hier &&
+	test_cmp_rev refs/remotes/nested/remote/fetch_hier HEAD
+'
+
+test_expect_success 'checkout --track=inherit,direct is rejected' '
+	test_must_fail git checkout --track=inherit,direct -b bad fetch_upstream/fetch_new 2>err &&
+	test_grep "cannot combine" err
+'
+
+test_expect_success 'checkout --track=fetch then --track=direct drops fetch (last-one-wins)' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_lastwin &&
+	test_commit -C fetch_upstream u_lastwin &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_lastwin &&
+	test_must_fail git checkout --track=fetch --track=direct \
+		-b local_lastwin fetch_upstream/fetch_lastwin &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_lastwin
+'
+
+test_expect_success 'checkout --track=fetch,inherit fetches and inherits' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_inherit &&
+	test_commit -C fetch_upstream u_inherit &&
+	git fetch fetch_upstream fetch_inherit &&
+	git checkout -b base_inherit fetch_upstream/fetch_inherit &&
+	test_commit -C fetch_upstream u_inherit2 &&
+	git checkout main &&
+	git checkout --track=fetch,inherit -b local_inherit base_inherit &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_inherit HEAD &&
+	test_cmp_config fetch_upstream branch.local_inherit.remote &&
+	test_cmp_config refs/heads/fetch_inherit branch.local_inherit.merge
+'
+
+test_expect_success 'checkout --track=bogus reports an error' '
+	git checkout main &&
+	test_must_fail git checkout --track=bogus -b bogus_branch fetch_upstream/fetch_new 2>err &&
+	test_grep "expects" err
+'
+
+test_expect_success 'switch --track=fetch -c picks up branch created upstream after clone' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_switch &&
+	test_commit -C fetch_upstream u_switch &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_switch &&
+	git switch --track=fetch -c local_switch fetch_upstream/fetch_switch &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_switch HEAD
+'
+
 test_done

base-commit: 68aca6b91299738150f71035d0033af6987fe455
-- 
gitgitgadget

^ permalink raw reply related

* Re: [PATCH 02/18] setup: stop using `the_repository` in `is_inside_worktree()`
From: Patrick Steinhardt @ 2026-05-18  7:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Tian Yuchen, git, Karthik Nayak, Elijah Newren
In-Reply-To: <xmqqa4txd2p5.fsf@gitster.g>

On Mon, May 18, 2026 at 08:41:10AM +0900, Junio C Hamano wrote:
> Tian Yuchen <cat@malon.dev> writes:
> 
> > On 4/20/26 16:22, Patrick Steinhardt wrote:
> >
> > I've noticed something that does not entirely convince me:
> >
> >> @@ -477,11 +476,13 @@ int is_inside_git_dir(void)
> >>  	return inside_git_dir;
> >>  }
> >>  
> >> -int is_inside_work_tree(void)
> >> +int is_inside_work_tree(struct repository *repo)
> >>  {
> >> -	if (inside_work_tree < 0)
> >> -		inside_work_tree = is_inside_dir(repo_get_work_tree(the_repository));
> >> -	return inside_work_tree;
> >> +	static struct strbuf buf = STRBUF_INIT;
> >> +	const char *worktree = repo_get_work_tree(repo);
> >> +	if (!worktree)
> >> +		return 0;
> >> +	return is_inside_dir(strbuf_realpath(&buf, worktree, 1));
> >>  }
> >> 
> >
> > Is it correct to statically allocate memory for the cache here? Could 
> > this lead
> > to memory overwriting issues in a multi-threaded environment?
> 
> I do not offhand know if other code paths that are called from this
> function are thread-safe, but yeah, this use of file-scope static is
> not a safe thing to do.

In the current status quo this is a safe conversion to do, as we still
have the assumption ingrained that this only works for a single repo.
We were using static variables before, and we're still using static
variables now.

That being said, I wouldn't mind dropping the static variable if this is
something we'd rather want to get rid of. It's a smell that I'm not
particularly happy about myself.

I'll send a revised version in a bit, thanks!

Patrick

^ permalink raw reply

* Re: [BUG] "git diff --word-diff" gives a diff while they are only space changes
From: Johannes Sixt @ 2026-05-18  7:30 UTC (permalink / raw)
  To: Michael Montalbo; +Cc: vincent, git, Junio C Hamano
In-Reply-To: <CAC2QwmKORPnsmV4SM_CnmhrbF+X754ae-n9m1fgjvVsL9d-wzg@mail.gmail.com>

Am 18.05.26 um 05:30 schrieb Michael Montalbo:
> On Thu, May 14, 2026 at 12:37 AM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Michael Montalbo <mmontalbo@gmail.com> writes:
>>
>>> @@ -457,6 +457,11 @@ endif::git-diff[]
>>>  +
>>>  Note that despite the name of the first mode, color is used to
>>>  highlight the changed parts in all modes if enabled.
>>> ++
>>> +Word diff works by finding word-level changes within each hunk of
>>> +the line-level diff.  The line-level alignment determines which
>>> +changed lines are compared to each other, which can affect the
>>> +word-level output.
>>
>> The added text may not say anything wrong, but I am not sure how it
>> helps the end user to know the way machinery works internally.
>>
> 
> I see what you mean. Maybe the doc should focus more on calling out
> the user-facing implication:
> 
>   `--word-diff` finds word-level changes within each hunk of the
>   line-level diff, so changes that only affect whitespace may still
>   appear in the output.
I don't know what this paragraph is trying to explain. I don't see how
this would explain Vincent's observed word-diff.

The thing is, "word-diff" is such a descriptive name for the operation
that it is difficult to find a description that is even better. The
manual page doesn't even give it a try. It defers to --word-diff-regex
right away, which then only talks about low-level details and doesn't
attempt to give a higher-level description what a word-diff is.

I don't think you can summarize the algorithm in a single sentence. But
then I have to ask: why write it down anyway? How does it help the
reader? Only so that they are able to derive an explanation for a
particular observed output? Would it have saved Vincent to write a bug
report?

If we document the algorithm in such detail, we cast it in stone. I
wouldn't want to paint ourselves into that corner.

-- Hannes


^ permalink raw reply

* Re: [BUG] "git diff --word-diff" gives a diff while they are only space changes
From: Michael Montalbo @ 2026-05-18  3:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: vincent, git, j6t
In-Reply-To: <xmqq8q9migqk.fsf@gitster.g>

On Thu, May 14, 2026 at 12:37 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Michael Montalbo <mmontalbo@gmail.com> writes:
>
> > @@ -457,6 +457,11 @@ endif::git-diff[]
> >  +
> >  Note that despite the name of the first mode, color is used to
> >  highlight the changed parts in all modes if enabled.
> > ++
> > +Word diff works by finding word-level changes within each hunk of
> > +the line-level diff.  The line-level alignment determines which
> > +changed lines are compared to each other, which can affect the
> > +word-level output.
>
> The added text may not say anything wrong, but I am not sure how it
> helps the end user to know the way machinery works internally.
>

I see what you mean. Maybe the doc should focus more on calling out
the user-facing implication:

  `--word-diff` finds word-level changes within each hunk of the
  line-level diff, so changes that only affect whitespace may still
  appear in the output.

I've intentionally omitted a whitespace workaround recommendation for
now given the ongoing discussion in the thread.

^ permalink raw reply

* [PATCH] fixup! approxidate: use deferred mday adjustments for "specials"
From: Tuomas Ahola @ 2026-05-18  2:40 UTC (permalink / raw)
  To: git; +Cc: Jeff King, Junio C Hamano, Tuomas Ahola
In-Reply-To: <20260516151540.9611-5-taahol@utu.fi>

Oops, let's reorder these lines.

Signed-off-by: Tuomas Ahola <taahol@utu.fi>
---
 t/t0006-date.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index b187b1bfc4..63e5628e05 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -212,9 +212,9 @@ check_approxidate 'noon today' '2009-08-30 12:00:00'
 check_approxidate 'today at noon' '2009-08-30 12:00:00' '-12 hours'
 check_approxidate 'noon today' '2009-09-01 12:00:00' '+36 hours'
 check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'
 check_approxidate 'last Friday at noon' '2009-08-28 12:00:00'
 check_approxidate 'last Friday at noon' '2009-08-28 12:00:00' '-12 hours'
-check_approxidate 'noon yesterday' '2009-08-29 12:00:00' '-12 hours'
 check_approxidate 'tea last saturday' '2009-08-29 17:00:00'
 check_approxidate 'tea last saturday' '2009-08-29 17:00:00' '-12 hours'
 check_approxidate 'January 5th noon pm' '2009-01-05 12:00:00'

base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
prerequisite-patch-id: 699b43ba1ff3235d54fa068f4c9b5ab5aa58eacc
prerequisite-patch-id: 2ab5ce03bce8b2953b418fe884f45fbd4a629337
prerequisite-patch-id: 52e3b766b9c6f148f09682b898bf5244b22ae0ba
prerequisite-patch-id: 1cd0e7498368f7c3b1833f12d56fefe732dd0413
-- 
2.30.2


^ permalink raw reply related

* Re: [PATCH 1/5] doc: convert git-bisect to synopsis style
From: Junio C Hamano @ 2026-05-18  2:10 UTC (permalink / raw)
  To: Jean-Noël Avila via GitGitGadget; +Cc: git, Jean-Noël Avila
In-Reply-To: <xmqq4ik5d0le.fsf@gitster.g>

Junio C Hamano <gitster@pobox.com> writes:

>> +[synopsis]
>>  ------------------------------------------------
>>  $ git bisect reset <commit>
>>  ------------------------------------------------
>
> and
>
>> +[synopsis]
>>  ------------------------------------------------
>>  git bisect old [<rev>]
>>  ------------------------------------------------
>
> were a bit surprising and confusing.  They are not exactly command
> syntax definitions (which is the SYNOPSIS section is about), but
> examples of usage.  The one with '$' command line prompt feels
> particularly confusing, as the prompt is not something that the
> end-user gives, unlike what we write in the synopsis section.
>
> Other than that, this is quite exciting.

Well, my local test with asciidoctor did not barf, but it seems that
the documentation pipeline run in GitHub Actions CI is unhappy.

https://github.com/git/git/actions/runs/26008649802/job/76444895183#step:4:4846

I do not know what the differences among the three environments
(counting your development environment---only one of which fails)
are offhand.


^ permalink raw reply

* What's cooking in git.git (May 2026, #04)
From: Junio C Hamano @ 2026-05-18  1:32 UTC (permalink / raw)
  To: git

Here are the topics that have been cooking in my tree.  Commits
prefixed with '+' are in 'next' (being in 'next' is a sign that a
topic is stable enough to be used and is a candidate to be in a
future release).  Commits prefixed with '-' are only in 'seen', and
aren't considered "accepted" at all and may be annotated with a URL
to a message that raises issues but they are by no means exhaustive.
A topic without enough support may be discarded after a long period
of no activity (of course they can be resubmitted when new interests
arise).

Copies of the source code to Git live in many repositories, and the
following is a list of the ones I push into or their mirrors.  Some
repositories have only a subset of branches.

With maint, master, next, seen, todo:

	git://git.kernel.org/pub/scm/git/git.git/
	git://repo.or.cz/alt-git.git/
	https://kernel.googlesource.com/pub/scm/git/git/
	https://github.com/git/git/
	https://gitlab.com/git-scm/git/

With all the integration branches and topics broken out:

	https://github.com/gitster/git/

Even though the preformatted documentation in HTML and man format
are not sources, they are published in these repositories for
convenience (replace "htmldocs" with "manpages" for the manual
pages):

	git://git.kernel.org/pub/scm/git/git-htmldocs.git/
	https://github.com/gitster/git-htmldocs.git/

Release tarballs are available at:

	https://www.kernel.org/pub/software/scm/git/

--------------------------------------------------
[Graduated to 'master']

* en/backfill-fixes-and-edges (2026-04-15) 3 commits
  (merged to 'next' on 2026-05-11 at baccbdf26d)
 + backfill: default to grabbing edge blobs too
 + backfill: document acceptance of revision-range in more standard manner
 + backfill: reject rev-list arguments that do not make sense
 (this branch is used by ds/path-walk-filters.)

 The 'git backfill' command now rejects revision-limiting options that
 are incompatible with its operation, uses standard documentation for
 revision ranges, and includes blobs from boundary commits by default
 to improve performance of subsequent operations.
 cf. <6e95b82a-19e3-460e-86f7-f899c2df261d@gmail.com>
 source: <pull.2088.git.1776297482.gitgitgadget@gmail.com>


* en/ort-cached-rename-with-trivial-resolution (2026-04-20) 1 commit
  (merged to 'next' on 2026-05-11 at 9fe24668d9)
 + merge-ort: handle cached rename & trivial resolution interaction better

 "ort" merge backend improvements.
 source: <pull.2095.git.1776724214171.gitgitgadget@gmail.com>


* hn/git-checkout-m-with-stash (2026-04-28) 5 commits
  (merged to 'next' on 2026-05-11 at 46adae5e5d)
 + checkout -m: autostash when switching branches
 + checkout: rollback lock on early returns in merge_working_tree
 + sequencer: teach autostash apply to take optional conflict marker labels
 + sequencer: allow create_autostash to run silently
 + stash: add --label-ours, --label-theirs, --label-base for apply

 "git checkout -m another-branch" was invented to deal with local
 changes to paths that are different between the current and the new
 branch, but it gave only one chance to resolve conflicts.  The command
 was taught to create a stash to save the local changes.
 source: <pull.2234.v16.git.git.1777401552.gitgitgadget@gmail.com>


* mf/format-patch-cover-letter-format-docfix (2026-04-22) 1 commit
  (merged to 'next' on 2026-05-11 at 3cca9cc117)
 + Fix docs for format.commitListFormat

 Docfix.
 source: <576d29f15e016889e02c253713656cd8cbf1f04c.1776894255.git.mroik@delayed.space>


* ps/clang-w-glibc-2.43-and-_Generic (2026-05-10) 1 commit
  (merged to 'next' on 2026-05-12 at fca23344f2)
 + build: tolerate use of _Generic from glibc 2.43 with Clang

 Headers from glibc 2.43 when used with clang does not allow
 disabling C11 language features, causing build failures..
 source: <xmqqqzniset2.fsf@gitster.g>


* pw/rename-to-get-current-worktree (2026-05-01) 1 commit
  (merged to 'next' on 2026-05-11 at 36ca1d49e2)
 + worktree: rename get_worktree_from_repository()

 Code clean-up.
 source: <bd48396137f8d1352d11b3bd2dca2848f24a347d.1777648798.git.phillip.wood@dunelm.org.uk>


* rs/grep-column-only-match-fix (2026-04-24) 1 commit
  (merged to 'next' on 2026-05-11 at a0d87e3104)
 + grep: fix --column --only-match for 2nd and later matches

 "git grep" update.
 source: <9bd69678-f04b-41d2-ad74-a386820d34c8@web.de>


* ss/t7004-unhide-git-failures (2026-04-20) 3 commits
  (merged to 'next' on 2026-05-11 at 9ee9feacb7)
 + t7004: avoid subshells to capture git exit codes
 + t7004: dynamically grab expected state in tests
 + t7004: drop hardcoded tag count for state verification

 Test clean-up.
 cf. <aecNc-BNwaqFlg5c@pks.im>
 source: <20260421053334.5414-1-r.siddharth.shrimali@gmail.com>

--------------------------------------------------
[New Topics]

* hn/status-pull-advice-qualified (2026-05-13) 1 commit
 - remote: qualify "git pull" advice for non-upstream branches

 Advice shown by "git status" when the local branch is behind or has
 diverged from its push branch has been updated to suggest "git pull
 <remote> <branch>".

 Comments?
 source: <pull.2301.v2.git.git.1778665812261.gitgitgadget@gmail.com>


* jk/dumb-http-alternate-fix (2026-05-12) 1 commit
  (merged to 'next' on 2026-05-17 at c1a51214fb)
 + http: handle absolute-path alternates from server root

 The HTTP walker misinterpreted the alternates file that gives an
 absolute path when the server URL does not have the final slash
 (i.e., "https://example.com" not "https://example.com/").

 Will merge to 'master'.
 source: <20260512162619.GA69813@coredump.intra.peff.net>


* jk/pretty-no-strbuf-presizing (2026-05-12) 1 commit
  (merged to 'next' on 2026-05-17 at ee684c614f)
 + pretty: drop strbuf pre-sizing from add_rfc2047()

 Remove ineffective strbuf presizing that would have computed an
 allocation that would not have fit in the available memory anyway,
 or too small due to integer wraparound to cause immediate automatic
 growing.

 Will merge to 'master'.
 source: <20260512162022.GA69669@coredump.intra.peff.net>


* kk/merge-octopus-optim (2026-05-11) 1 commit
 - merge: use repo_in_merge_bases for octopus up-to-date check

 The logic to determine that branches in an octopus merge are
 independent has been optimized.

 Comments?
 source: <pull.2110.git.1778566286543.gitgitgadget@gmail.com>


* rs/strbuf-add-uint (2026-05-12) 4 commits
 - ls-tree: use strbuf_add_uint()
 - ls-files: use strbuf_add_uint()
 - cat-file: use strbuf_add_uint()
 - strbuf: add strbuf_add_uint()

 Adding a decimal integer with strbuf_addf("%u") appears commonly;
 they have been optimized by using a custom formatter.

 Comments?
 source: <20260512115603.80780-1-l.s.r@web.de>


* ta/approxidate-noon-fix (2026-05-16) 4 commits
 - approxidate: use deferred mday adjustments for "specials"
 - approxidate: make "specials" respect fixed day-of-month
 - t0006: add support for approxidate test date adjustment
 - approxidate: make "today" wrap to midnight

 "Friday noon" asked in the morning on Sunday was parsed to be one
 day before the specified time, which has been corrected.

 Comments?
 source: <20260516151540.9611-1-taahol@utu.fi>


* mm/doc-word-diff (2026-05-13) 1 commit
 - doc: clarify that --word-diff operates on line-level hunks

 The documentation for "--word-diff" has been extended with a bit of
 implementation detail of where these different words come from.

 Comments?
 source: <pull.2113.git.1778686956622.gitgitgadget@gmail.com>


* rs/strbuf-add-oid-hex (2026-05-13) 1 commit
 - hex: add and use strbuf_add_oid_hex()

 Formatting object name in full hexadecimal form has been optimized
 by using a new strbuf_add_oid_hex() helper function.

 Comments?
 source: <183aa0fd-d455-4ec9-9c42-d511fac8b3e4@web.de>


* kk/limit-list-optim (2026-05-14) 1 commit
 - revision: use priority queue in limit_list()

 The limit_list() function that is one of the core part of the
 revision traversal infrastructure has been optimized by replacing
 its use of linear list with priority queue.

 Will merge to 'next'.
 source: <pull.2114.git.1778777491939.gitgitgadget@gmail.com>


* ed/check-connected-close-err-fd (2026-05-16) 1 commit
 - Merge branch 'ed/check-connected-close-err-fd-2.53' into ed/check-connected-close-err-fd
 (this branch uses ed/check-connected-close-err-fd-2.53.)

 File descriptor leak fix.

 Will merge to 'next'?
 (this branch uses ed/check-connected-close-err-fd-2.53.)


* ed/check-connected-close-err-fd-2.53 (2026-05-14) 1 commit
 - connected: close err_fd in promisor fast-path
 (this branch is used by ed/check-connected-close-err-fd.)

 File descriptor leak fix (for 2.54 maintenance track).

 Will be merged together with ed/check-connected-close-err-fd topic.
 source: <pull.2303.git.git.1778827194448.gitgitgadget@gmail.com>


* jk/apply-leakfix (2026-05-15) 1 commit
 - apply: plug leak on "patch too large" error

 Leakfix.

 Will merge to 'next'.
 source: <20260516021622.GA744303@coredump.intra.peff.net>


* jk/commit-sign-overflow-fix (2026-05-15) 1 commit
 - commit: handle large commit messages in utf8 verification

 Leakfix.

 Will merge to 'next'.
 source: <20260516022310.GB744303@coredump.intra.peff.net>


* kk/tips-reachable-from-bases-optim (2026-05-16) 2 commits
 - t6600: add tests for duplicate tips in tips_reachable_from_bases()
 - commit-reach: use object flags for tips_reachable_from_bases()

 Revision traversal optimization.

 Will merge to 'next'?
 source: <pull.2116.v3.git.1778947182.gitgitgadget@gmail.com>


* pb/doc-diff-format-updates (2026-05-15) 3 commits
 - diff-format.adoc: mode and hash are 0* for unmerged paths from index only
 - diff-format.adoc: 'git diff-files' prints two lines for unmerged files
 - diff-format.adoc: remove mention of diff-tree specific output

 Doc updates.

 Will merge to 'next'.
 source: <pull.2304.git.git.1778860091.gitgitgadget@gmail.com>


* ps/t3903-cover-stash-include-untracked (2026-05-16) 1 commit
 - stash: add coverage for show --include-untracked

 Test coverage has been added to "git stash --include-untracked".

 Will merge to 'next'.
 source: <20260516183347.4323-2-pushkarkumarsingh1970@gmail.com>


* rs/trailer-fold-optim (2026-05-15) 1 commit
 - trailer: change strbuf in-place in unfold_value()

 Code simplification.

 Will merge to 'next'.
 source: <816be07e-2cd6-48fe-ae93-57fa0f2543ed@web.de>


* rs/use-builtin-add-overflow-explicitly-on-clang (2026-05-14) 2 commits
 - use __builtin_add_overflow() in st_add() with Clang
 - strbuf: use st_add3() in strbuf_grow()

 Micro optimization of codepaths that compute allocation sizes carefully.

 What's the status of this topic?
 source: <0ded6062-f66a-4713-af24-d1b5aa654823@web.de>
 source: <c6e9b337-c4fc-4cbd-ac32-e8d3814749b0@web.de>


* tc/generate-configlist-fix-for-older-ninja (2026-05-15) 1 commit
 - generate-configlist: collapse depfile for older Ninja

 Build update.

 Will merge to 'next'?
 source: <20260515-toon-fix-almalinux8-v3-1-b545a0647f0f@iotcl.com>


* hn/config-typo-advice (2026-05-16) 1 commit
 - config: suggest the correct form when key contains "=" in set context

 "git config foo.bar=baz" is not likely to be a request to read the
 value of such a variable with '=' in its name; rather it is plausible
 that the user meant "git config set foo.bar baz".  Give advice when
 giving an error message.

 Comments?
 source: <pull.2302.v2.git.git.1778935976330.gitgitgadget@gmail.com>


* ja/doc-synopsis-style-again (2026-05-17) 5 commits
 - doc: convert git-imap-send synopsis and options to new style
 - doc: convert git-apply synopsis and options to new style
 - doc: convert git-am synopsis and options to new style
 - doc: convert git-grep synopsis and options to new style
 - doc: convert git-bisect to synopsis style

 A batch of documentation pages has been updated to use the modern
 synopsis style.

 Comments?
 source: <pull.2117.git.1779049615.gitgitgadget@gmail.com>


* kn/refs-fsck-skip-lock-files (2026-05-17) 1 commit
 - refs/files: skip lock files during consistency checks

 The consistency checks for the files reference backend have been updated
 to skip lock files earlier, avoiding unnecessary parsing of
 intermediate files.

 Will merge to 'next'.
 source: <20260517-refs-fsck-skip-lock-files-v3-1-b24dfd673c7e@gmail.com>


* jt/config-lock-timeout (2026-05-17) 1 commit
 - config: retry acquiring config.lock, configurable via core.configLockTimeout

 Configuration file locking now retries for a short period, avoiding
 failures when multiple processes attempt to update the configuration
 simultaneously.

 Comments?
 cf. <xmqqzf1xbl4i.fsf@gitster.g>
 source: <20260517132111.1014901-1-joerg@thalheim.io>

--------------------------------------------------
[Cooking]

* sp/shallow-deepen-on-non-shallow-repo-fix (2026-05-11) 1 commit
  (merged to 'next' on 2026-05-15 at 67dd491aae)
 + shallow: fix relative deepen on non-shallow repositories

 "git fetch --deepen=<n>" in a full clone truncated the history to <n>
 commits deep, which has been corrected to be a no-op instead.

 Will merge to 'master'.
 source: <20260511192044.169557-1-samo_pogacnik@t-2.net>


* ag/sequencer-remove-unused-struct-member (2026-05-11) 1 commit
  (merged to 'next' on 2026-05-17 at 8553437ae1)
 + sequencer: remove todo_add_branch_context.commit

 Code clean-up.

 Will merge to 'master'.
 cf. <agLKVn6RF4UBYd_8@pks.im>
 source: <pull.2111.git.1778502113485.gitgitgadget@gmail.com>


* ps/maintenance-daemonize-lockfix (2026-05-13) 2 commits
 - run-command: honor "gc.auto" for auto-maintenance
 - builtin/maintenance: fix locking with "--detach"

 "git maintenance" that goes background did not use the lockfile to
 prevent multiple maintenance processes from running at the same
 time, which has been corrected..

 Comments?
 source: <20260513-pks-maintenance-fix-lock-with-detach-v3-0-f27a1ac82891@pks.im>


* js/mingw-no-nedmalloc (2026-05-08) 3 commits
  (merged to 'next' on 2026-05-13 at 2116a6bcc9)
 + mingw: remove the vendored compat/nedmalloc/ subtree
 + mingw: drop the build-system plumbing for nedmalloc
 + mingw: stop using nedmalloc

 Stop using unmaintained custom allocator in Windows build which was
 the last user of the code.

 Will merge to 'master'.
 source: <pull.2104.v3.git.1778244661.gitgitgadget@gmail.com>


* sj/submodule-update-clone-config-fix (2026-05-09) 1 commit
  (merged to 'next' on 2026-05-12 at 5a0094838a)
 + submodule-config: fix reading submodule.fetchJobs

 The configuration variable submodule.fetchJobs was not read correctly,
 which has been corrected.

 Will merge to 'master'.
 source: <pull.2287.v4.git.git.1778385022964.gitgitgadget@gmail.com>


* aw/validate-proxy-url-scheme (2026-05-05) 1 commit
  (merged to 'next' on 2026-05-15 at da9c1b71d7)
 + http: reject unsupported proxy URL schemes

 Misspelt proxy URL (e.g., httt://...) did not trigger any warning
 or failure, which has been corrected.

 Will merge to 'master'.
 source: <20260505091941.1825-2-aminnimaj@gmail.com>


* hn/branch-prune-merged (2026-05-13) 5 commits
 - branch: add --all-remotes flag
 - branch: add branch.<name>.pruneMerged opt-out
 - branch: add --prune-merged <remote>
 - branch: let delete_branches warn instead of error on bulk refusal
 - branch: add --forked <remote>

 "git branch" command learned "--prune-merged" option to remove
 local branches that have already been merged to the remote-tracking
 branches they track.

 Comments?
 source: <pull.2285.v9.git.git.1778700883.gitgitgadget@gmail.com>


* kh/doc-restore-double-underscores-fix (2026-05-05) 1 commit
  (merged to 'next' on 2026-05-12 at 2e8fc7cdac)
 + doc: restore: remove double underscore

 Doc update.

 Will merge to 'master'.
 source: <double_underscore.670@msgid.xyz>


* mm/diff-U-takes-no-negative-values (2026-05-12) 4 commits
  (merged to 'next' on 2026-05-17 at d81439a049)
 + parse-options: clarify what "negated" means for PARSE_OPT_NONEG
 + xdiff: guard against negative context lengths
 + diff: reject negative values for -U/--unified
 + diff: reject negative values for --inter-hunk-context

 The command line parser for "git diff" learned a few options take
 only non-negative integers.

 Will merge to 'master'.
 source: <pull.2105.v2.git.1778609423.gitgitgadget@gmail.com>


* mm/git-url-parse (2026-05-01) 8 commits
  (merged to 'next' on 2026-05-15 at 416deceeeb)
 + t9904: add tests for the new url-parse builtin
 + doc: describe the url-parse builtin
 + builtin: create url-parse command
 + urlmatch: define url_parse function
 + url: return URL_SCHEME_UNKNOWN instead of dying
 + url: move scheme detection to URL header/source
 + url: move url_is_local_not_ssh to url.h
 + connect: rename enum protocol to url_scheme

 The internal URL parsing logic has been made accessible via a new
 subcommand "git url-parse".

 Will merge to 'master'.
 cf. <xmqqjyt9p9pk.fsf@gitster.g>
 cf. <20260512085734.GA26769@tb-raspi4>
 source: <pull.1715.v3.git.git.1777699722.gitgitgadget@gmail.com>


* kh/doc-commit-graph (2026-05-07) 1 commit
  (merged to 'next' on 2026-05-12 at b9cafeb32d)
 + doc: add caveat about turning off commit-graph

 Ramifications of turning off commit-graph has been documented a bit
 more clearly.

 Will merge to 'master'.
 source: <V3_caveat_commit-graph.6b6@msgid.xyz>


* dk/doc-exclude-is-shared-per-repo (2026-05-12) 1 commit
  (merged to 'next' on 2026-05-17 at ddc761aec6)
 + ignore: note info/exclude lives in GIT_COMMON_DIR, not GIT_DIR

 Document the fact that .git/info/exclude is shared across worktrees
 linked to the same repository.

 Will merge to 'master'.
 cf. <bea48414-217b-4860-9279-fe94e3687c28@gmail.com>
 source: <ec97ad3f054e90b675f099a36a81a23bb4b2a0ed.1778620784.git.ben.knoble+github@gmail.com>


* kk/paint-down-to-common-optim (2026-05-11) 2 commits
  (merged to 'next' on 2026-05-17 at 2e39c767e5)
 + commit-reach: early exit paint_down_to_common for single merge-base
 + commit-reach: introduce merge_base_flags enum

 "git merge-base" optimization.

 Will merge to 'master'.
 source: <pull.2109.v4.git.1778504352.gitgitgadget@gmail.com>


* st/daemon-sockaddr-fixes (2026-05-14) 3 commits
 - daemon: guard NULL REMOTE_PORT in execute() logging
 - daemon: fix IPv6 address truncation in ip2str()
 - daemon: fix IPv6 address corruption in lookup_hostname()

 Correct use of sockaddr API in "git daemon".

 Waiting for response(s) to review comment(s).
 cf. <agGLRC1ziF5F8Okh@pks.im>
 source: <pull.2300.git.git.1778773592.gitgitgadget@gmail.com>


* ag/rebase-update-refs-limit-to-branches (2026-05-10) 1 commit
  (merged to 'next' on 2026-05-12 at 5222da09bb)
 + rebase: ignore non-branch update-refs

 "git rebase --update-refs", when used with an rebase.instructionFormat
 with "%d" (describe) in it, tried to update local branch HEAD by
 mistake, which has been corrected.

 Will merge to 'master'.
 source: <20260510224111.64467-2-mail@abhinavg.net>


* jc/ci-enable-expensive (2026-05-10) 2 commits
  (merged to 'next' on 2026-05-15 at d258bb5e55)
 + ci: enable EXPENSIVE for contributor builds
 + Merge branch 'js/objects-larger-than-4gb-on-windows' into jc/ci-enable-expensive
 (this branch uses js/objects-larger-than-4gb-on-windows.)

 Enable expensive tests to catch topics that may cause breakages on
 integration branches closer to their origin in the contributor PR
 builds.

 Will merge to 'master'.
 source: <xmqqjyta9630.fsf@gitster.g>


* rs/sideband-clear-line-before-print (2026-05-10) 1 commit
  (merged to 'next' on 2026-05-12 at 83880f8ce6)
 + sideband: clear full line when printing remote messages

 Tweak the way how sideband messages from remote are printed while
 we talk with a remote repository to avoid tickling terminal
 emulator glitches.

 Will merge to 'master'.
 source: <9826dabf-c9a6-4397-8ae6-a24f9c507f1b@web.de>


* jh/alias-i18n-fixes (2026-04-24) 1 commit
  (merged to 'next' on 2026-05-13 at c7cd30d414)
 + alias: restore support for simple dotted aliases

 Further update to the i18n alias support to avoid regressions.

 Will merge to 'master'.
 source: <20260424161707.1514255-1-jonatan@jontes.page>


* kn/refs-generic-helpers (2026-05-04) 9 commits
  (merged to 'next' on 2026-05-15 at 62cb4e0ce2)
 + refs: use peeled tag values in reference backends
 + refs: add peeled object ID to the `ref_update` struct
 + refs: move object parsing to the generic layer
 + update-ref: handle rejections while adding updates
 + update-ref: move `print_rejected_refs()` up
 + refs: return `ref_transaction_error` from `ref_transaction_update()`
 + refs: extract out reflog config to generic layer
 + refs: introduce `ref_store_init_options`
 + refs: remove unused typedef 'ref_transaction_commit_fn'

 Refactor service routines in the ref subsystem backends.

 Will merge to 'master'.
 cf. <afmFmGo_Sg33Rv6V@pks.im>
 cf. <87o6isqq4q.fsf@toon--20250203-5JQV3.mail-host-address-is-not-set>
 source: <20260504-refs-move-to-generic-layer-v4-0-936ac2f0b1a3@gmail.com>


* ob/more-repo-config-values (2026-04-23) 8 commits
 - env: move "warn_on_object_refname_ambiguity" into `struct repo_config_values`
 - env: move "sparse_expect_files_outside_of_patterns" into `repo_config_values`
 - env: move "core_sparse_checkout_cone" into `struct repo_config_values`
 - environment: move "precomposed_unicode" into `struct repo_config_values`
 - environment: move "pack_compression_level" into `struct repo_config_values`
 - environment: move `zlib_compression_level` into `struct repo_config_values`
 - environment: move "check_stat" into `struct repo_config_values`
 - environment: move "trust_ctime" into `struct repo_config_values`

 Expecting a reroll.
 cf. <CAD=f0L8-_3sDGGkCzF4WA0xmUtaY_qiz__3zq5AemLgwTsqvsg@mail.gmail.com>
 cf. <xmqqlddqu013.fsf@gitster.g>
 source: <20260423165432.143598-1-belkid98@gmail.com>


* ps/history-fixup (2026-04-26) 3 commits
  (merged to 'next' on 2026-05-13 at e6154b6272)
 + builtin/history: introduce "fixup" subcommand
 + builtin/history: generalize function to commit trees
 + replay: allow callers to control what happens with empty commits

 "git history" learned "fixup" command.

 Will merge to 'master'.
 cf. <xmqq33zxp4aq.fsf@gitster.g>
 source: <20260427-b4-pks-history-fixup-v3-0-cb908f06264b@pks.im>


* sb/unpack-index-pack-buffer-resize (2026-04-28) 1 commit
  (merged to 'next' on 2026-05-13 at 2edd54bcfe)
 + index-pack, unpack-objects: increase input buffer from 4 KiB to 128 KiB

 Use a larger buffer size in the code paths to ingest pack stream.

 Will merge to 'master'.
 cf. <xmqqy0hpnpkb.fsf@gitster.g>
 source: <pull.2282.v4.git.git.1777387660841.gitgitgadget@gmail.com>


* bc/sign-commit-with-custom-encoding (2026-04-27) 2 commits
  (merged to 'next' on 2026-05-13 at e82a4966c0)
 + commit: sign commit after mutating buffer
 + commit: name UTF-8 function appropriately

 Signing commit with custom encoding was passing the data to be
 signed at a wrong stage in the pipeline, which has been corrected.

 Will merge to 'master'.
 cf. <xmqqtssdnpf7.fsf@gitster.g>
 source: <20260427221834.1824543-1-sandals@crustytoothpaste.net>


* cc/promisor-auto-config-url-more (2026-04-27) 9 commits
 - doc: promisor: improve acceptFromServer entry
 - promisor-remote: auto-configure unknown remotes
 - promisor-remote: trust known remotes matching acceptFromServerUrl
 - promisor-remote: introduce promisor.acceptFromServerUrl
 - promisor-remote: add 'local_name' to 'struct promisor_info'
 - urlmatch: add url_normalize_pattern() helper
 - urlmatch: change 'allow_globs' arg to bool
 - t5710: simplify 'mkdir X' followed by 'git -C X init'
 - Merge branch 'cc/promisor-auto-config-url' into cc/promisor-auto-config-url-more

 The handling of promisor-remote protocol capability has been
 loosened to allow the other side to add to the list of promisor
 remotes via the promisor.acceptFromServerURL configuration
 variable.

 Waiting for response(s) to review comment(s).
 cf. <875x4yoys5.fsf@toon--20250203-5JQV3.mail-host-address-is-not-set>
 source: <20260427124108.3524129-1-christian.couder@gmail.com>


* js/objects-larger-than-4gb-on-windows (2026-05-08) 11 commits
  (merged to 'next' on 2026-05-13 at 843d2ac470)
 + ci: run expensive tests on push builds to integration branches
 + t5608: mark >4GB tests as EXPENSIVE
 + test-tool synthesize: add precomputed SHA-256 pack for 4 GiB + 1
 + test-tool synthesize: precompute pack for 4 GiB + 1
 + test-tool synthesize: use the unsafe hash for speed
 + t5608: add regression test for >4GB object clone
 + test-tool: add a helper to synthesize large packfiles
 + delta, packfile: use size_t for delta header sizes
 + odb, packfile: use size_t for streaming object sizes
 + git-zlib: handle data streams larger than 4GB
 + index-pack, unpack-objects: use size_t for object size
 (this branch is used by jc/ci-enable-expensive.)

 Update code paths that assumed "unsigned long" was long enough for
 "size_t".

 Will merge to 'master'.
 source: <pull.2102.v3.git.1778228209.gitgitgadget@gmail.com>


* kh/doc-log-decorate-list (2026-04-27) 2 commits
  (merged to 'next' on 2026-05-15 at f740311a37)
 + doc: log: use the same delimiter in description list
 + doc: log: fix --decorate description list

 Doc update.

 Will merge to 'master'.
 cf. <xmqqpl31np0l.fsf@gitster.g>
 source: <CV_doc_log_--decorate_list.626@msgid.xyz>


* za/t2000-modernise-more (2026-04-29) 1 commit
  (merged to 'next' on 2026-05-15 at 3b524d0ba5)
 + t2000: consolidate second scenario into a single test block

 Test update.

 Will merge to 'master'.
 cf. <xmqqfr3xnofn.fsf@gitster.g>
 source: <20260429103607.406339-1-zakariyahali100@gmail.com>


* hn/checkout-track-fetch (2026-05-11) 1 commit
 - checkout: extend --track with a "fetch" mode to refresh start-point

 "git checkout --track=..." learned to optionally fetch the branch
 from the remote the new branch will work with.

 Comments?
 cf. <xmqqh5odqxh2.fsf@gitster.g>
 source: <pull.2281.v8.git.git.1778507225500.gitgitgadget@gmail.com>


* mf/revision-max-count-oldest (2026-05-15) 1 commit
 - revision.c: implement --max-count-oldest

 "git rev-list" (and "git log" family of commands) learned a new "--max-count-oldest"
 that picks oldest N commits in the range instead of the usual newest.

 Will merge to 'next'?
 source: <463cc8e2764edb7de3d379f615f5cfbd0919bfa3.1778887662.git.mroik@delayed.space>


* mm/line-log-cleanup (2026-04-27) 3 commits
 - line-log: allow non-patch diff formats with -L
 - line-log: integrate -L output with the standard log-tree pipeline
 - revision: move -L setup before output_format-to-diff derivation

 Code clean-up.

 Comments?
 cf. <xmqqfr3xp98b.fsf@gitster.g>
 source: <pull.2094.git.1777349126.gitgitgadget@gmail.com>


* ds/path-walk-filters (2026-05-13) 14 commits
 - path-walk: support `combine` filter
 - path-walk: support `object:type` filter
 - path-walk: support `tree:0` filter
 - t6601: tag otherwise-unreachable trees
 - pack-objects: support sparse:oid filter with path-walk
 - path-walk: add pl_sparse_trees to control tree pruning
 - path-walk: support blob size limit filter
 - backfill: die on incompatible filter options
 - path-walk: support blobless filter
 - path-walk: always emit directly-requested objects
 - t/perf: add pack-objects filter and path-walk benchmark
 - pack-objects: pass --objects with --path-walk
 - t5620: make test work with path-walk var
 - Merge branch 'en/backfill-fixes-and-edges' into ds/path-walk-filters

 The "git pack-objects --path-walk" traversal has been integrated
 with several object filters, including blobless and sparse filters.

 Comments?
 source: <pull.2101.v4.git.1778707135.gitgitgadget@gmail.com>


* en/ort-harden-against-corrupt-trees (2026-04-20) 5 commits
 - cache-tree: fix verify_cache() to catch non-adjacent D/F conflicts
 - merge-ort: abort merge when trees have duplicate entries
 - merge-ort: free diff pairs queue in clear_or_reinit_internal_opts()
 - merge-ort: drop unnecessary show_all_errors from collect_merge_info()
 - merge-ort: propagate callback errors from traverse_trees_wrapper()

 "ort" merge backend handles merging corrupt trees better by
 aborting when it should.

 Needs review.
 source: <pull.2096.git.1776731171.gitgitgadget@gmail.com>


* sg/t6112-unwanted-tilde-expansion-fix (2026-04-21) 1 commit
  (merged to 'next' on 2026-05-12 at ad2d08eb44)
 + t6112: avoid tilde expansion

 Test fix.

 Will merge to 'master'.
 source: <20260421192132.51172-1-szeder.dev@gmail.com>


* pw/status-rebase-todo (2026-05-01) 2 commits
 - status: improve rebase todo list parsing
 - sequencer: factor out parsing of todo commands

 The display of the rebase todo list in "git status" has been
 improved to correctly abbreviate object IDs for more commands and
 avoid misinterpreting refs as object IDs.

 Needs review.
 source: <cover.1777648598.git.phillip.wood@dunelm.org.uk>


* tb/pseudo-merge-bugfixes (2026-05-11) 9 commits
 - pack-bitmap: prevent pattern leak on pseudo-merge re-assignment
 - Documentation: fix broken `sampleRate` in gitpacking(7)
 - pack-bitmap: reject pseudo-merge "sampleRate" of 0
 - pack-bitmap: parse commits in `find_pseudo_merge_group_for_ref()`
 - pack-bitmap: fix pseudo-merge lookup for shared commits
 - pack-bitmap: fix inverted binary search in `pseudo_merge_at()`
 - pack-bitmap-write: sort pseudo-merge commit lookup table in pack order
 - t5333: demonstrate various pseudo-merge bugs
 - t/helper: add 'test-tool bitmap write' subcommand

 Fixes many bugs in pseudo-merge code.

 Will merge to 'next'.
 source: <cover.1778546804.git.me@ttaylorr.com>


* ds/fetch-negotiation-options (2026-05-14) 8 commits
 - send-pack: pass negotiation config in push
 - remote: add remote.*.negotiationInclude config
 - fetch: add --negotiation-include option for negotiation
 - negotiator: add have_sent() interface
 - remote: add remote.*.negotiationRestrict config
 - transport: rename negotiation_tips
 - fetch: add --negotiation-restrict option
 - t5516: fix test order flakiness

 The negotiation tip options in "git fetch" have been reworked to
 allow requiring certain refs to be sent as "have" lines, and to
 restrict negotiation to a specific set of refs.

 Comments?
 source: <pull.2085.v4.git.1778762495.gitgitgadget@gmail.com>


* mc/http-emptyauth-negotiate-fix (2026-04-30) 4 commits
  (merged to 'next' on 2026-05-12 at 843ae82cd0)
 + doc: clarify http.emptyAuth values
  (merged to 'next' on 2026-04-20 at 6539524ca2)
 + t5563: add tests for http.emptyAuth with Negotiate
 + http: attempt Negotiate auth in http.emptyAuth=auto mode
 + http: extract http_reauth_prepare() from retry paths

 The 'http.emptyAuth=auto' configuration now correctly attempts
 Negotiate authentication before falling back to manual credentials.
 This allows seamless Kerberos ticket-based authentication without
 requiring users to explicitly set 'http.emptyAuth=true'.

 Will merge to 'master'.
 source: <e0f236767f81ea60f90749d1bc00ab78081efd0e.1777546472.git.gitgitgadget@gmail.com>
 source: <pull.2087.git.1776331259.gitgitgadget@gmail.com>


* en/batch-prefetch (2026-05-14) 4 commits
 - grep: prefetch necessary blobs
 - builtin/log: prefetch necessary blobs for `git cherry`
 - patch-ids.h: add missing trailing parenthesis in documentation comment
 - promisor-remote: document caller filtering contract

 In a lazy clone, "git cherry" and "git grep" often fetch necessary
 blob objects one by one from promisor remotes.  It has been corrected
 to collect necessary object names and fetch them in bulk to gain
 reasonable performance.

 Comments?
 source: <pull.2089.v3.git.1778775928.gitgitgadget@gmail.com>


* en/diffstat-utf8-truncation-fix (2026-04-20) 1 commit
  (merged to 'next' on 2026-05-13 at adf801eb1d)
 + diff: fix out-of-bounds reads and NULL deref in diffstat UTF-8 truncation

 The computation to shorten the filenames shown in diffstat measured
 width of individual UTF-8 characters to add up, but forgot to take
 into account error cases (e.g., an invalid UTF-8 sequence, or a
 control character).

 Will merge to 'master'.
 source: <pull.2093.v3.git.1776699778177.gitgitgadget@gmail.com>


* ps/odb-in-memory (2026-04-10) 18 commits
 - t/unit-tests: add tests for the in-memory object source
 - odb: generic in-memory source
 - odb/source-inmemory: stub out remaining functions
 - odb/source-inmemory: implement `freshen_object()` callback
 - odb/source-inmemory: implement `count_objects()` callback
 - odb/source-inmemory: implement `find_abbrev_len()` callback
 - odb/source-inmemory: implement `for_each_object()` callback
 - odb/source-inmemory: convert to use oidtree
 - oidtree: add ability to store data
 - cbtree: allow using arbitrary wrapper structures for nodes
 - odb/source-inmemory: implement `write_object_stream()` callback
 - odb/source-inmemory: implement `write_object()` callback
 - odb/source-inmemory: implement `read_object_stream()` callback
 - odb/source-inmemory: implement `read_object_info()` callback
 - odb: fix unnecessary call to `find_cached_object()`
 - odb/source-inmemory: implement `free()` callback
 - odb: introduce "in-memory" source
 - Merge branch 'jt/odb-transaction-write' into ps/odb-in-memory
 (this branch uses jt/odb-transaction-write.)

 Add a new odb "in-memory" source that is meant to only hold
 tentative objects (like the virtual blob object that represents the
 working tree file used by "git blame").

 Need to wait for the base topic.
 source: <20260410-b4-pks-odb-source-inmemory-v3-0-22fd0fad58fe@pks.im>


* js/adjust-tests-to-explicitly-access-bare-repo (2026-04-26) 8 commits
  (merged to 'next' on 2026-05-13 at 48695e1cb0)
 + safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
 + status tests: filter `.gitconfig` from status output
 + ls-files tests: filter `.gitconfig` from `--others` output
 + t5601: restore `.gitconfig` after includeIf test
 + t1305: use `--git-dir=.` for bare repo in include cycle test
 + t1300: remove global config settings injected by test-lib.sh
 + t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
 + test-lib: allow bare repository access when breaking changes are enabled

 Some tests assume that bare repository accesses are by default
 allowed; rewrite some of them to avoid the assumption, rewrite
 others to explicitly set safe.bareRepository to allow them.

 Will merge to 'master'.
 source: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>


* cl/conditional-config-on-worktree-path (2026-05-13) 2 commits
 - config: add "worktree" and "worktree/i" includeIf conditions
 - config: refactor include_by_gitdir() into include_by_path()

 The [includeIf "condition"] conditional inclusion facility for
 configuration files has learned to use the location of worktree
 in its condition.

 Will merge to 'next'?
 cf. <2989eb07-2933-4b5a-9e5c-33ef9b805528@gmail.com>
 source: <20260513-includeif-worktree-v4-0-f8e6212d1fba@black-desk.cn>


* ps/shift-root-in-graph (2026-04-27) 1 commit
 - graph: add indentation for commits preceded by a parentless commit

 In a history with more than one root commit, "git log --graph
 --oneline" stuffed an unrelated commit immediately below a root
 commit, which has been corrected by making the spot below a root
 unavailable.

 Waiting for response(s) to review comment(s).
 cf. <20260513230216.GA1378627@coredump.intra.peff.net>
 source: <20260427102838.44867-2-pabloosabaterr@gmail.com>


* lp/repack-propagate-promisor-debugging-info (2026-04-18) 6 commits
 - repack-promisor: add missing headers
 - t7703: test for promisor file content after geometric repack
 - t7700: test for promisor file content after repack
 - repack-promisor: preserve content of promisor files after repack
 - repack-promisor add helper to fill promisor file after repack
 - pack-write: add explanation to promisor file content

 When fetching objects into a lazily cloned repository, .promisor
 files are created with information meant to help debugging.  "git
 repack" has been taught to carry this information forward to
 packfiles that are newly created.

 Needs review.
 cf. <xmqqse7xm8av.fsf@gitster.g>
 source: <cover.1776384902.git.lorenzo.pegorari2002@gmail.com>


* th/promisor-quiet-per-repo (2026-04-06) 1 commit
 - promisor-remote: fix promisor.quiet to use the correct repository

 The "promisor.quiet" configuration variable was not used from
 relevant submodules when commands like "grep --recurse-submodules"
 triggered a lazy fetch, which has been corrected.

 Comments?
 source: <20260406183041.783800-1-vikingtc4@gmail.com>


* jt/odb-transaction-write (2026-05-14) 7 commits
 - odb/transaction: make `write_object_stream()` pluggable
 - object-file: generalize packfile writes to use odb_write_stream
 - object-file: avoid fd seekback by checking object size upfront
 - object-file: remove flags from transaction packfile writes
 - odb: update `struct odb_write_stream` read() callback
 - odb/transaction: use pluggable `begin_transaction()`
 - odb: split `struct odb_transaction` into separate header
 (this branch is used by ps/odb-in-memory.)

 ODB transaction interface is being reworked to explicitly handle
 object writes.

 Will merge to 'next'?
 source: <20260514183740.1505171-1-jltobler@gmail.com>


* sa/cat-file-batch-mailmap-switch (2026-04-15) 1 commit
 - cat-file: add mailmap subcommand to --batch-command

 "git cat-file --batch" learns an in-line command "mailmap"
 that lets the user toggle use of mailmap.

 Will merge to 'next'?
 source: <20260416033250.4327-2-siddharthasthana31@gmail.com>


* tb/incremental-midx-part-3.3 (2026-04-29) 16 commits
 - repack: allow `--write-midx=incremental` without `--geometric`
 - repack: introduce `--write-midx=incremental`
 - repack: implement incremental MIDX repacking
 - packfile: ensure `close_pack_revindex()` frees in-memory revindex
 - builtin/repack.c: convert `--write-midx` to an `OPT_CALLBACK`
 - repack-geometry: prepare for incremental MIDX repacking
 - repack-midx: extract `repack_fill_midx_stdin_packs()`
 - repack-midx: factor out `repack_prepare_midx_command()`
 - midx: expose `midx_layer_contains_pack()`
 - repack: track the ODB source via existing_packs
 - midx: support custom `--base` for incremental MIDX writes
 - midx: introduce `--no-write-chain-file` for incremental MIDX writes
 - midx: use `strvec` for `keep_hashes`
 - midx: build `keep_hashes` array in order
 - midx: use `strset` for retained MIDX files
 - midx-write: handle noop writes when converting incremental chains

 The repacking code has been refactored and compaction of MIDX layers
 have been implemented, and incremental strategy that does not require
 all-into-one repacking has been introduced.

 Waiting for response(s) to review comment(s).
 cf. <agTw579yuy4iHoMq@szeder.dev>
 cf. <20260513230825.GA1378716@coredump.intra.peff.net>
 source: <cover.1777507303.git.me@ttaylorr.com>


* jd/unpack-trees-wo-the-repository (2026-03-31) 2 commits
 - unpack-trees: use repository from index instead of global
 - unpack-trees: use repository from index instead of global

 A handful of inappropriate uses of the_repository have been
 rewritten to use the right repository structure instance in the
 unpack-trees.c codepath.

 Comments?
 source: <pull.2258.v2.git.git.1774971267.gitgitgadget@gmail.com>


* ps/setup-wo-the-repository (2026-04-20) 18 commits
 - setup: stop using `the_repository` in `init_db()`
 - setup: stop using `the_repository` in `create_reference_database()`
 - setup: stop using `the_repository` in `initialize_repository_version()`
 - setup: stop using `the_repository` in `check_repository_format()`
 - setup: stop using `the_repository` in `upgrade_repository_format()`
 - setup: stop using `the_repository` in `setup_git_directory()`
 - setup: stop using `the_repository` in `setup_git_directory_gently()`
 - setup: stop using `the_repository` in `setup_git_env()`
 - setup: stop using `the_repository` in `set_git_work_tree()`
 - setup: stop using `the_repository` in `setup_work_tree()`
 - setup: stop using `the_repository` in `enter_repo()`
 - setup: stop using `the_repository` in `verify_non_filename()`
 - setup: stop using `the_repository` in `verify_filename()`
 - setup: stop using `the_repository` in `path_inside_repo()`
 - setup: stop using `the_repository` in `prefix_path()`
 - setup: stop using `the_repository` in `is_inside_git_dir()`
 - setup: stop using `the_repository` in `is_inside_worktree()`
 - setup: replace use of `the_repository` in static functions

 Many uses of the_repository has been updated to use a more
 appropriate struct repository instance in setup.c codepath.

 Needs review.
 source: <20260420-pks-setup-wo-the-repository-v1-0-f4a81c4988e8@pks.im>


* kh/doc-trailers (2026-04-13) 9 commits
 - doc: interpret-trailers: document comment line treatment
 - doc: interpret-trailers: commit to “trailer block” term
 - doc: interpret-trailers: add key format example
 - doc: interpret-trailers: explain key format
 - doc: interpret-trailers: explain the format after the intro
 - doc: interpret-trailers: not just for commit messages
 - doc: interpret-trailers: use “metadata” in Name as well
 - doc: interpret-trailers: replace “lines” with “metadata”
 - doc: interpret-trailers: stop fixating on RFC 822

 Documentation updates.

 Needs review.
 cf. <xmqq1pfivfa3.fsf@gitster.g>
 source: <V2_CV_doc_int-tr_key_format.613@msgid.xyz>


* ps/graph-lane-limit (2026-03-27) 3 commits
 - graph: add truncation mark to capped lanes
 - graph: add --graph-lane-limit option
 - graph: limit the graph width to a hard-coded max

 The graph output from commands like "git log --graph" can now be
 limited to a specified number of lanes, preventing overly wide output
 in repositories with many branches.

 Will merge to 'next'?
 cf. <bdff0a5d-b738-4053-9b72-08eba88156de@kdbg.org>
 source: <20260328001113.1275291-1-pabloosabaterr@gmail.com>


* jr/bisect-custom-terms-in-output (2026-05-14) 3 commits
 - rev-parse: use selected alternate terms to look up refs
 - bisect: print bisect terms in single quotes
 - bisect: use selected alternate terms in status output

 "git bisect" now uses the selected terms (e.g., old/new) more
 consistently in its output.

 Will merge to 'next'?
 source: <20260514-bisect-terms-v4-0-b3e3cf1b06ce@schlaraffenlan.de>


* ua/push-remote-group (2026-05-03) 3 commits
 - push: support pushing to a remote group
 - remote: move remote group resolution to remote.c
 - remote: fix sign-compare warnings in push_cas_option

 "git push" learned to take a "remote group" name to push to, which
 causes pushes to multiple places, just like "git fetch" would do.

 Waiting for response(s) to review comment(s).
 cf. <d6566004-f803-4824-b050-f086b6d6d76c@app.fastmail.com>
 source: <20260503153402.1333220-1-usmanakinyemi202@gmail.com>


* kh/name-rev-custom-format (2026-05-11) 5 commits
  (merged to 'next' on 2026-05-12 at c944d6131e)
 + format-rev: introduce builtin for on-demand pretty formatting
 + name-rev: make dedicated --annotate-stdin --name-only test
 + name-rev: factor code for sharing with a new command
 + name-rev: run clang-format before factoring code
 + name-rev: wrap both blocks in braces

 A new builtin "git format-rev" is introduced for pretty formatting
 one revision expression per line or commit object names found in
 running text.

 Will merge to 'master'.
 source: <V5_CV_format-rev.6c9@msgid.xyz>


* js/parseopt-subcommand-autocorrection (2026-04-27) 11 commits
 - SQUASH???
 - doc: document autocorrect API
 - parseopt: add tests for subcommand autocorrection
 - parseopt: enable subcommand autocorrection for git-remote and git-notes
 - parseopt: autocorrect mistyped subcommands
 - autocorrect: provide config resolution API
 - autocorrect: rename AUTOCORRECT_SHOW to AUTOCORRECT_HINT
 - autocorrect: use mode and delay instead of magic numbers
 - help: move tty check for autocorrection to autocorrect.c
 - help: make autocorrect handling reusable
 - parseopt: extract subcommand handling from parse_options_step()

 The parse-options library learned to auto-correct misspelled
 subcommand names.

 Expecting a reroll.
 cf. <xmqqcxz2tzpr.fsf@gitster.g>
 source: <SY0P300MB0801677A2A1E0FD38D06A841CE2A2@SY0P300MB0801.AUSP300.PROD.OUTLOOK.COM>


* jc/neuter-sideband-post-3.0 (2026-03-05) 2 commits
 - sideband: delay sanitizing by default to Git v3.0
 - Merge branch 'jc/neuter-sideband-fixup' into jc/neuter-sideband-post-3.0

 The final step, split from earlier attempt by Dscho, to loosen the
 sideband restriction for now and tighten later at Git v3.0 boundary.

 On hold to help the base topic with wider exposure.
 (this branch uses jc/neuter-sideband-fixup.)
 source: <20260305233452.3727126-8-gitster@pobox.com>


* cs/subtree-split-recursion (2026-03-05) 3 commits
 - contrib/subtree: reduce recursion during split
 - contrib/subtree: functionalize split traversal
 - contrib/subtree: reduce function side-effects

 When processing large history graphs on Debian or Ubuntu, "git
 subtree" can die with a "recursion depth reached" error.

 Comments?
 source: <20260305-cs-subtree-split-recursion-v2-0-7266be870ba9@howdoi.land>


* pt/fsmonitor-linux (2026-04-15) 13 commits
 - fsmonitor: convert shown khash to strset in do_handle_client
 - fsmonitor: add tests for Linux
 - fsmonitor: add timeout to daemon stop command
 - fsmonitor: close inherited file descriptors and detach in daemon
 - run-command: add close_fd_above_stderr option
 - fsmonitor: implement filesystem change listener for Linux
 - fsmonitor: rename fsm-settings-darwin.c to fsm-settings-unix.c
 - fsmonitor: rename fsm-ipc-darwin.c to fsm-ipc-unix.c
 - fsmonitor: use pthread_cond_timedwait for cookie wait
 - compat/win32: add pthread_cond_timedwait
 - fsmonitor: fix hashmap memory leak in fsmonitor_run_daemon
 - fsmonitor: fix khash memory leak in do_handle_client
 - t9210, t9211: disable GIT_TEST_SPLIT_INDEX for scalar clone tests

 The fsmonitor daemon has been implemented for Linux.

 Will merge to 'next'?
 cf. <xmqqa4u5nnxq.fsf@gitster.g>
 source: <pull.2147.v15.git.git.1776259657.gitgitgadget@gmail.com>


* pw/xdiff-shrink-memory-consumption (2026-05-04) 5 commits
  (merged to 'next' on 2026-05-15 at 7a867909d2)
 + xdiff: reduce the size of array
 + xprepare: simplify error handling
 + xdiff: cleanup xdl_clean_mmatch()
 + xdiff: reduce size of action arrays
 + Merge branch 'en/xdiff-cleanup-3' into pw/xdiff-shrink-memory-consumption
 (this branch uses en/xdiff-cleanup-3.)

 Shrink wasted memory in Myers diff that does not account for common
 prefix and suffix removal.

 Will merge to 'master'.
 source: <cover.1777903579.git.phillip.wood@dunelm.org.uk>


* en/xdiff-cleanup-3 (2026-04-29) 6 commits
  (merged to 'next' on 2026-05-12 at e4e72e0f34)
 + xdiff/xdl_cleanup_records: make execution of action easier to follow
 + xdiff/xdl_cleanup_records: make setting action easier to follow
 + xdiff/xdl_cleanup_records: make limits more clear
 + xdiff/xdl_cleanup_records: use unambiguous types
 + xdiff: use unambiguous types in xdl_bogo_sqrt()
 + xdiff/xdl_cleanup_records: delete local recs pointer
 (this branch is used by pw/xdiff-shrink-memory-consumption.)

 Preparation of the xdiff/ codebase to work with Rust.

 Will merge to 'master'.
 source: <pull.2156.v6.git.git.1777500495.gitgitgadget@gmail.com>

^ permalink raw reply

* Re: [PATCH v2] config: retry acquiring config.lock, configurable via core.configLockTimeout
From: Junio C Hamano @ 2026-05-18  0:46 UTC (permalink / raw)
  To: Joerg Thalheim; +Cc: git, Patrick Steinhardt, Johannes Schindelin
In-Reply-To: <20260517132111.1014901-1-joerg@thalheim.io>

Joerg Thalheim <joerg@thalheim.io> writes:

> +/*
> + * How long to retry acquiring config.lock when another process holds
> + * it. Default matches core.packedRefsTimeout; override via
> + * core.configLockTimeout.
> + */
> +static long config_lock_timeout_ms(struct repository *r)
> +{
> +	static int configured;
> +	static int timeout_ms = 1000;
> +
> +	if (!configured) {
> +		repo_config_get_int(r, "core.configlocktimeout", &timeout_ms);
> +		configured = 1;
> +	}
> +
> +	return timeout_ms;
> +}

The above design means whichever repository happens to be passed for
the first time as "r" to this call will fix the return value from
the function for the rest of the system, meaning that the lock timeout
is a per-process property and the repository parameter passed to the
function does not matter all that much.

It may make sense to admit that this is not a per-repository
property (due to the use of local caching), have the function take
no parameter and use the_repository to the config_get call.  That
would make the intention more clear.

Of course the other end of the spectrum is to get rid of the
"configured" caching here, and ask the config system to make a
hashtable look-up every time the function is called.  That will keep
the lock timeout per-repository, which is closer to what the current
function signature suggests.

I dunno.  My gut feeling is that there aren't valid reasons why you
would want to specifically set different timeout values per
repository, so the simplicity of using the_repository (i.e. the
primary repository instance this process deals with) sounds like a
better way to go.

Thanks.

^ permalink raw reply

* Re: [PATCH 1/5] doc: convert git-bisect to synopsis style
From: Junio C Hamano @ 2026-05-18  0:26 UTC (permalink / raw)
  To: Jean-Noël Avila via GitGitGadget; +Cc: git, Jean-Noël Avila
In-Reply-To: <dca7f192f1e5cdfb57682feace0a4b3a10204376.1779049615.git.gitgitgadget@gmail.com>

"Jean-Noël Avila via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>
>
> Convert Documentation/git-bisect.adoc to the modern synopsis style.
>
> - Replace [verse] with [synopsis] in the SYNOPSIS block

This was expected.

> - Remove single quotes around command names in the synopsis
> - Use backticks for inline commands, options, refs, and special values
> - Apply [synopsis] attribute to in-body command-form code blocks

This is very much unexpected.  I think everybody thought [synopsis]
was invented to be used for the SYNOPSIS section at the beginning of
each manual page, and ...

>  SYNOPSIS
>  --------
> -[verse]
> -'git bisect' start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
> -		   [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
> ...
> -'git bisect' help
> +[synopsis]
> +git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
> +		 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
> ...
> +git bisect help

... a change like this is very much expected and understandable, but
new appearances of [synonsis] in places like:

> +[synopsis]
>  ------------------------------------------------
>  $ git bisect reset <commit>
>  ------------------------------------------------

and

> +[synopsis]
>  ------------------------------------------------
>  git bisect old [<rev>]
>  ------------------------------------------------

were a bit surprising and confusing.  They are not exactly command
syntax definitions (which is the SYNOPSIS section is about), but
examples of usage.  The one with '$' command line prompt feels
particularly confusing, as the prompt is not something that the
end-user gives, unlike what we write in the synopsis section.

Other than that, this is quite exciting.


^ permalink raw reply

* Re: [PATCH 02/18] setup: stop using `the_repository` in `is_inside_worktree()`
From: Junio C Hamano @ 2026-05-17 23:41 UTC (permalink / raw)
  To: Tian Yuchen; +Cc: Patrick Steinhardt, git, Karthik Nayak, Elijah Newren
In-Reply-To: <4d9604e2-036b-485e-aa93-53c35d539bed@malon.dev>

Tian Yuchen <cat@malon.dev> writes:

> On 4/20/26 16:22, Patrick Steinhardt wrote:
>
> I've noticed something that does not entirely convince me:
>
>> @@ -477,11 +476,13 @@ int is_inside_git_dir(void)
>>  	return inside_git_dir;
>>  }
>>  
>> -int is_inside_work_tree(void)
>> +int is_inside_work_tree(struct repository *repo)
>>  {
>> -	if (inside_work_tree < 0)
>> -		inside_work_tree = is_inside_dir(repo_get_work_tree(the_repository));
>> -	return inside_work_tree;
>> +	static struct strbuf buf = STRBUF_INIT;
>> +	const char *worktree = repo_get_work_tree(repo);
>> +	if (!worktree)
>> +		return 0;
>> +	return is_inside_dir(strbuf_realpath(&buf, worktree, 1));
>>  }
>> 
>
> Is it correct to statically allocate memory for the cache here? Could 
> this lead
> to memory overwriting issues in a multi-threaded environment?

I do not offhand know if other code paths that are called from this
function are thread-safe, but yeah, this use of file-scope static is
not a safe thing to do.

^ permalink raw reply

* Re: [PATCH] revision: use priority queue in limit_list()
From: Junio C Hamano @ 2026-05-17 23:31 UTC (permalink / raw)
  To: Kristofer Karlsson
  Cc: Derrick Stolee, Jeff King, Kristofer Karlsson via GitGitGadget,
	git
In-Reply-To: <CAL71e4MxhcZqxPVEe38Shuqt7h5dxLDGi66hN2cFXnmg-POKWA@mail.gmail.com>

Kristofer Karlsson <krka@spotify.com> writes:

> I don't want to pollute this patch with that change - should I start a
> separate thread for it or just revisit this later?
> (Perhaps I have too many optimization patches in flux already)

Thanks for a great news.  I agree that it is a good idea to find a
good stopping point and make improvements step-wise, and the patch
posted for limit_list() is probably such a good stopping point.

If we do not see further comments on the current patch, let's merge
it to 'next', cook it for the standard 7 calendar days or so before
merging it down to 'master'.  Further optimizations can be made on
top of the updated 'master' branch as new and separate topics.

^ permalink raw reply

* [PATCH 5/5] doc: convert git-imap-send synopsis and options to new style
From: Jean-Noël Avila via GitGitGadget @ 2026-05-17 20:26 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila, Jean-Noël Avila
In-Reply-To: <pull.2117.git.1779049615.gitgitgadget@gmail.com>

From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Convert git-imap-send from [verse]/single-quote style to the modern
synopsis-block style:

- Replace [verse] with [synopsis] in SYNOPSIS block
- Backtick-quote all OPTIONS terms
- Backtick-quote all config keys in config/imap.adoc
- Backtick-quote bare config key references in prose

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Documentation/config/imap.adoc   | 30 +++++++++++++++---------------
 Documentation/git-imap-send.adoc | 24 ++++++++++++------------
 2 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 4682a6bd03..cb8f5e2700 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,44 +1,44 @@
-imap.folder::
+`imap.folder`::
 	The folder to drop the mails into, which is typically the Drafts
 	folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
 	`[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
 	the value of this configuration variable is used as the fallback
 	default value when the `--folder` option is not given.
 
-imap.tunnel::
+`imap.tunnel`::
 	Command used to set up a tunnel to the IMAP server through which
 	commands will be piped instead of using a direct network connection
-	to the server. Required when imap.host is not set.
+	to the server. Required when `imap.host` is not set.
 
-imap.host::
+`imap.host`::
 	A URL identifying the server. Use an `imap://` prefix for non-secure
 	connections and an `imaps://` prefix for secure connections.
-	Ignored when imap.tunnel is set, but required otherwise.
+	Ignored when `imap.tunnel` is set, but required otherwise.
 
-imap.user::
+`imap.user`::
 	The username to use when logging in to the server.
 
-imap.pass::
+`imap.pass`::
 	The password to use when logging in to the server.
 
-imap.port::
+`imap.port`::
 	An integer port number to connect to on the server.
-	Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
-	Ignored when imap.tunnel is set.
+	Defaults to 143 for `imap://` hosts and 993 for `imaps://` hosts.
+	Ignored when `imap.tunnel` is set.
 
-imap.sslverify::
+`imap.sslverify`::
 	A boolean to enable/disable verification of the server certificate
 	used by the SSL/TLS connection. Default is `true`. Ignored when
-	imap.tunnel is set.
+	`imap.tunnel` is set.
 
-imap.preformattedHTML::
+`imap.preformattedHTML`::
 	A boolean to enable/disable the use of html encoding when sending
-	a patch.  An html encoded patch will be bracketed with <pre>
+	a patch.  An html encoded patch will be bracketed with `<pre>`
 	and have a content type of text/html.  Ironically, enabling this
 	option causes Thunderbird to send the patch as a plain/text,
 	format=fixed email.  Default is `false`.
 
-imap.authMethod::
+`imap.authMethod`::
 	Specify the authentication method for authenticating with the IMAP server.
 	If Git was built with the NO_CURL option, or if your curl version is older
 	than 7.34.0, or if you're running git-imap-send with the `--no-curl`
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 278e5ccd36..538b91afc0 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -8,9 +8,9 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
 
 SYNOPSIS
 --------
-[verse]
-'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
-'git imap-send' --list
+[synopsis]
+git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+git imap-send --list
 
 
 DESCRIPTION
@@ -32,30 +32,30 @@ $ git format-patch --signoff --stdout --attach origin | git imap-send
 OPTIONS
 -------
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Be verbose.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Be quiet.
 
--f <folder>::
---folder=<folder>::
+`-f <folder>`::
+`--folder=<folder>`::
 	Specify the folder in which the emails have to saved.
 	For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
 
---curl::
+`--curl`::
 	Use libcurl to communicate with the IMAP server, unless tunneling
 	into it.  Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
 	option set.
 
---no-curl::
+`--no-curl`::
 	Talk to the IMAP server using git's own IMAP routines instead of
 	using libcurl.  Ignored if Git was built with the NO_OPENSSL option
 	set.
 
---list::
+`--list`::
 	Run the IMAP LIST command to output a list of all the folders present.
 
 CONFIGURATION
-- 
gitgitgadget

^ permalink raw reply related

* [PATCH 4/5] doc: convert git-apply synopsis and options to new style
From: Jean-Noël Avila via GitGitGadget @ 2026-05-17 20:26 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila, Jean-Noël Avila
In-Reply-To: <pull.2117.git.1779049615.gitgitgadget@gmail.com>

From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Convert git-apply from [verse]/single-quote style to the modern
synopsis-block style:

- Replace [verse] with [synopsis] in SYNOPSIS block
- Backtick-quote all OPTIONS terms and config keys in config/apply.adoc
- Convert single-quoted inline commands ('git apply', 'diff', etc.)
- Wrap standalone placeholders in underscores (<n>, <root>, <action>)
- Backtick-quote `*.rej` and GNU `patch` tool references

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Documentation/config/apply.adoc |  17 +++--
 Documentation/git-apply.adoc    | 125 ++++++++++++++++----------------
 2 files changed, 74 insertions(+), 68 deletions(-)

diff --git a/Documentation/config/apply.adoc b/Documentation/config/apply.adoc
index f9908e210a..36fcea6291 100644
--- a/Documentation/config/apply.adoc
+++ b/Documentation/config/apply.adoc
@@ -1,11 +1,16 @@
-apply.ignoreWhitespace::
-	When set to 'change', tells 'git apply' to ignore changes in
+`apply.ignoreWhitespace`::
+	When set to `change`, tells `git apply` to ignore changes in
 	whitespace, in the same way as the `--ignore-space-change`
 	option.
-	When set to one of: no, none, never, false, it tells 'git apply' to
+	When set to one of: `no`, `none`, `never`, `false`, it tells `git apply` to
 	respect all whitespace differences.
+ifndef::git-apply[]
 	See linkgit:git-apply[1].
+endif::git-apply[]
 
-apply.whitespace::
-	Tells 'git apply' how to handle whitespace, in the same way
-	as the `--whitespace` option. See linkgit:git-apply[1].
+`apply.whitespace`::
+	Tells `git apply` how to handle whitespace, in the same way
+	as the `--whitespace` option.
+ifndef::git-apply[]
+	See linkgit:git-apply[1].
+endif::git-apply[]
diff --git a/Documentation/git-apply.adoc b/Documentation/git-apply.adoc
index 6c71ee69da..3f22dac1ce 100644
--- a/Documentation/git-apply.adoc
+++ b/Documentation/git-apply.adoc
@@ -8,8 +8,8 @@ git-apply - Apply a patch to files and/or to the index
 
 SYNOPSIS
 --------
-[verse]
-'git apply' [--stat] [--numstat] [--summary] [--check]
+[synopsis]
+git apply [--stat] [--numstat] [--summary] [--check]
 	  [--index | --intent-to-add] [--3way] [--ours | --theirs | --union]
 	  [--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
 	  [--allow-binary-replacement | --binary] [--reject] [-z]
@@ -35,33 +35,33 @@ linkgit:git-format-patch[1] and/or received by email.
 
 OPTIONS
 -------
-<patch>...::
-	The files to read the patch from.  '-' can be used to read
+`<patch>...`::
+	The files to read the patch from.  `-` can be used to read
 	from the standard input.
 
---stat::
+`--stat`::
 	Instead of applying the patch, output diffstat for the
 	input.  Turns off "apply".
 
---numstat::
+`--numstat`::
 	Similar to `--stat`, but shows the number of added and
 	deleted lines in decimal notation and the pathname without
 	abbreviation, to make it more machine friendly.  For
 	binary files, outputs two `-` instead of saying
 	`0 0`.  Turns off "apply".
 
---summary::
+`--summary`::
 	Instead of applying the patch, output a condensed
 	summary of information obtained from git diff extended
 	headers, such as creations, renames, and mode changes.
 	Turns off "apply".
 
---check::
+`--check`::
 	Instead of applying the patch, see if the patch is
 	applicable to the current working tree and/or the index
 	file and detects errors.  Turns off "apply".
 
---index::
+`--index`::
 	Apply the patch to both the index and the working tree (or
 	merely check that it would apply cleanly to both if `--check` is
 	in effect). Note that `--index` expects index entries and
@@ -70,13 +70,13 @@ OPTIONS
 	raise an error if they are not, even if the patch would apply
 	cleanly to both the index and the working tree in isolation.
 
---cached::
+`--cached`::
 	Apply the patch to just the index, without touching the working
 	tree. If `--check` is in effect, merely check that it would
 	apply cleanly to the index entry.
 
--N::
---intent-to-add::
+`-N`::
+`--intent-to-add`::
 	When applying the patch only to the working tree, mark new
 	files to be added to the index later (see `--intent-to-add`
 	option in linkgit:git-add[1]). This option is ignored if
@@ -84,8 +84,8 @@ OPTIONS
 	repository. Note that `--index` could be implied by other options
 	such as `--3way`.
 
--3::
---3way::
+`-3`::
+`--3way`::
 	Attempt 3-way merge if the patch records the identity of blobs it is supposed
 	to apply to and we have those blobs available locally, possibly leaving the
 	conflict markers in the files in the working tree for the user to
@@ -94,14 +94,14 @@ OPTIONS
 	When used with the `--cached` option, any conflicts are left at higher stages
 	in the cache.
 
---ours::
---theirs::
---union::
+`--ours`::
+`--theirs`::
+`--union`::
 	Instead of leaving conflicts in the file, resolve conflicts favouring
-	our (or their or both) side of the lines. Requires --3way.
+	our (or their or both) side of the lines. Requires `--3way`.
 
---build-fake-ancestor=<file>::
-	Newer 'git diff' output has embedded 'index information'
+`--build-fake-ancestor=<file>`::
+	Newer `git diff` output has embedded 'index information'
 	for each blob to help identify the original version that
 	the patch applies to.  When this flag is given, and if
 	the original versions of the blobs are available locally,
@@ -110,18 +110,18 @@ OPTIONS
 When a pure mode change is encountered (which has no index information),
 the information is read from the current index instead.
 
--R::
---reverse::
+`-R`::
+`--reverse`::
 	Apply the patch in reverse.
 
---reject::
-	For atomicity, 'git apply' by default fails the whole patch and
+`--reject`::
+	For atomicity, `git apply` by default fails the whole patch and
 	does not touch the working tree when some of the hunks
 	do not apply.  This option makes it apply
 	the parts of the patch that are applicable, and leave the
-	rejected hunks in corresponding *.rej files.
+	rejected hunks in corresponding `*.rej` files.
 
--z::
+`-z`::
 	When `--numstat` has been given, do not munge pathnames,
 	but use a NUL-terminated machine-readable format.
 +
@@ -129,20 +129,20 @@ Without this option, pathnames with "unusual" characters are quoted as
 explained for the configuration variable `core.quotePath` (see
 linkgit:git-config[1]).
 
--p<n>::
-	Remove <n> leading path components (separated by slashes) from
+`-p<n>`::
+	Remove _<n>_ leading path components (separated by slashes) from
 	traditional diff paths. E.g., with `-p2`, a patch against
 	`a/dir/file` will be applied directly to `file`. The default is
 	1.
 
--C<n>::
-	Ensure at least <n> lines of surrounding context match before
+`-C<n>`::
+	Ensure at least _<n>_ lines of surrounding context match before
 	and after each change.  When fewer lines of surrounding
 	context exist they all must match.  By default no context is
 	ever ignored.
 
---unidiff-zero::
-	By default, 'git apply' expects that the patch being
+`--unidiff-zero`::
+	By default, `git apply` expects that the patch being
 	applied is a unified diff with at least one line of context.
 	This provides good safety measures, but breaks down when
 	applying a diff generated with `--unified=0`. To bypass these
@@ -151,34 +151,34 @@ linkgit:git-config[1]).
 Note, for the reasons stated above, the usage of context-free patches is
 discouraged.
 
---apply::
+`--apply`::
 	If you use any of the options marked "Turns off
-	'apply'" above, 'git apply' reads and outputs the
+	'apply'" above, `git apply` reads and outputs the
 	requested information without actually applying the
 	patch.  Give this flag after those flags to also apply
 	the patch.
 
---no-add::
+`--no-add`::
 	When applying a patch, ignore additions made by the
 	patch.  This can be used to extract the common part between
-	two files by first running 'diff' on them and applying
+	two files by first running `diff` on them and applying
 	the result with this option, which would apply the
 	deletion part but not the addition part.
 
---allow-binary-replacement::
---binary::
+`--allow-binary-replacement`::
+`--binary`::
 	Historically we did not allow binary patch application
 	without an explicit permission from the user, and this
 	flag was the way to do so.  Currently, we always allow binary
 	patch application, so this is a no-op.
 
---exclude=<path-pattern>::
-	Don't apply changes to files matching the given path pattern. This can
+`--exclude=<path-pattern>`::
+	Don't apply changes to files matching _<path-pattern>_. This can
 	be useful when importing patchsets, where you want to exclude certain
 	files or directories.
 
---include=<path-pattern>::
-	Apply changes to files matching the given path pattern. This can
+`--include=<path-pattern>`::
+	Apply changes to files matching the _<path-pattern>_. This can
 	be useful when importing patchsets, where you want to include certain
 	files or directories.
 +
@@ -188,15 +188,15 @@ patch to each path is used.  A patch to a path that does not match any
 include/exclude pattern is used by default if there is no include pattern
 on the command line, and ignored if there is any include pattern.
 
---ignore-space-change::
---ignore-whitespace::
+`--ignore-space-change`::
+`--ignore-whitespace`::
 	When applying a patch, ignore changes in whitespace in context
 	lines if necessary.
 	Context lines will preserve their whitespace, and they will not
 	undergo whitespace fixing regardless of the value of the
 	`--whitespace` option. New lines will still be fixed, though.
 
---whitespace=<action>::
+`--whitespace=<action>`::
 	When applying a patch, detect a new or modified line that has
 	whitespace errors.  What are considered whitespace errors is
 	controlled by `core.whitespace` configuration.  By default,
@@ -209,7 +209,7 @@ By default, the command outputs warning messages but applies the patch.
 When `git-apply` is used for statistics and not applying a
 patch, it defaults to `nowarn`.
 +
-You can use different `<action>` values to control this
+You can use different _<action>_ values to control this
 behavior:
 +
 * `nowarn` turns off the trailing whitespace warning.
@@ -223,48 +223,48 @@ behavior:
   to apply the patch.
 * `error-all` is similar to `error` but shows all errors.
 
---inaccurate-eof::
-	Under certain circumstances, some versions of 'diff' do not correctly
+`--inaccurate-eof`::
+	Under certain circumstances, some versions of `diff` do not correctly
 	detect a missing new-line at the end of the file. As a result, patches
-	created by such 'diff' programs do not record incomplete lines
+	created by such `diff` programs do not record incomplete lines
 	correctly. This option adds support for applying such patches by
 	working around this bug.
 
--v::
---verbose::
+`-v`::
+`--verbose`::
 	Report progress to stderr. By default, only a message about the
 	current patch being applied will be printed. This option will cause
 	additional information to be reported.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Suppress stderr output. Messages about patch status and progress
 	will not be printed.
 
---recount::
+`--recount`::
 	Do not trust the line counts in the hunk headers, but infer them
 	by inspecting the patch (e.g. after editing the patch without
 	adjusting the hunk headers appropriately).
 
---directory=<root>::
-	Prepend <root> to all filenames.  If a "-p" argument was also passed,
+`--directory=<root>`::
+	Prepend _<root>_ to all filenames.  If a `-p` argument was also passed,
 	it is applied before prepending the new root.
 +
 For example, a patch that talks about updating `a/git-gui.sh` to `b/git-gui.sh`
 can be applied to the file in the working tree `modules/git-gui/git-gui.sh` by
 running `git apply --directory=modules/git-gui`.
 
---unsafe-paths::
+`--unsafe-paths`::
 	By default, a patch that affects outside the working area
 	(either a Git controlled working tree, or the current working
-	directory when "git apply" is used as a replacement of GNU
-	patch) is rejected as a mistake (or a mischief).
+	directory when `git apply` is used as a replacement of GNU
+	`patch`) is rejected as a mistake (or a mischief).
 +
-When `git apply` is used as a "better GNU patch", the user can pass
+When `git apply` is used as a "better GNU `patch`", the user can pass
 the `--unsafe-paths` option to override this safety check.  This option
 has no effect when `--index` or `--cached` is in use.
 
---allow-empty::
+`--allow-empty`::
 	Don't return an error for patches containing no diff. This includes
 	empty patches and patches with commit text only.
 
@@ -273,11 +273,12 @@ CONFIGURATION
 
 include::includes/cmd-config-section-all.adoc[]
 
+:git-apply: 1
 include::config/apply.adoc[]
 
 SUBMODULES
 ----------
-If the patch contains any changes to submodules then 'git apply'
+If the patch contains any changes to submodules then `git apply`
 treats these changes as follows.
 
 If `--index` is specified (explicitly or implicitly), then the submodule
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH 3/5] doc: convert git-am synopsis and options to new style
From: Jean-Noël Avila via GitGitGadget @ 2026-05-17 20:26 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila, Jean-Noël Avila
In-Reply-To: <pull.2117.git.1779049615.gitgitgadget@gmail.com>

From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Convert git-am from [verse]/single-quote style to the modern
synopsis-block style:

- Replace [verse] with [synopsis] in SYNOPSIS block
- Backtick-quote all OPTIONS terms
- Convert inline man page refs
- Convert inline command refs
- Convert prose placeholders:

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Documentation/config/am.adoc                  |   6 +-
 Documentation/format-patch-caveats.adoc       |   2 +-
 .../format-patch-end-of-commit-message.adoc   |   4 +-
 Documentation/git-am.adoc                     | 132 +++++++++---------
 4 files changed, 72 insertions(+), 72 deletions(-)

diff --git a/Documentation/config/am.adoc b/Documentation/config/am.adoc
index e9561e12d7..250e6b5047 100644
--- a/Documentation/config/am.adoc
+++ b/Documentation/config/am.adoc
@@ -1,11 +1,11 @@
-am.keepcr::
+`am.keepcr`::
 	If true, linkgit:git-am[1] will call linkgit:git-mailsplit[1]
 	for patches in mbox format with parameter `--keep-cr`. In this
 	case linkgit:git-mailsplit[1] will
 	not remove `\r` from lines ending with `\r\n`. Can be overridden
 	by giving `--no-keep-cr` from the command line.
 
-am.threeWay::
+`am.threeWay`::
 	By default, linkgit:git-am[1] will fail if the patch does not
 	apply cleanly. When set to true, this setting tells
 	linkgit:git-am[1] to fall back on 3-way merge if the patch
@@ -13,7 +13,7 @@ am.threeWay::
 	have those blobs available locally (equivalent to giving the
 	`--3way` option from the command line). Defaults to `false`.
 
-am.messageId::
+`am.messageId`::
 	Add a `Message-ID` trailer based on the email header to the
 	commit when using linkgit:git-am[1] (see
 	linkgit:git-interpret-trailers[1]). See also the `--message-id`
diff --git a/Documentation/format-patch-caveats.adoc b/Documentation/format-patch-caveats.adoc
index 807a65b885..133e4757e7 100644
--- a/Documentation/format-patch-caveats.adoc
+++ b/Documentation/format-patch-caveats.adoc
@@ -28,6 +28,6 @@ repositories. This goes to show that this behavior does not only impact
 email workflows.
 
 Given these limitations, one might be tempted to use a general-purpose
-utility like patch(1) instead. However, patch(1) will not only look for
+utility like `patch`(1) instead. However, `patch`(1) will not only look for
 unindented diffs (like linkgit:git-am[1]) but will try to apply indented
 diffs as well.
diff --git a/Documentation/format-patch-end-of-commit-message.adoc b/Documentation/format-patch-end-of-commit-message.adoc
index ec1ef79f5e..a1a624d2ac 100644
--- a/Documentation/format-patch-end-of-commit-message.adoc
+++ b/Documentation/format-patch-end-of-commit-message.adoc
@@ -1,8 +1,8 @@
 Any line that is of the form:
 
 * three-dashes and end-of-line, or
-* a line that begins with "diff -", or
-* a line that begins with "Index: "
+* a line that begins with `diff -`, or
+* a line that begins with `Index: `
 
 is taken as the beginning of a patch, and the commit log message
 is terminated before the first occurrence of such a line.
diff --git a/Documentation/git-am.adoc b/Documentation/git-am.adoc
index ac65852918..28adf4cf65 100644
--- a/Documentation/git-am.adoc
+++ b/Documentation/git-am.adoc
@@ -8,17 +8,17 @@ git-am - Apply a series of patches from a mailbox
 
 SYNOPSIS
 --------
-[verse]
-'git am' [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--[no-]verify]
+[synopsis]
+git am [--signoff] [--keep] [--[no-]keep-cr] [--[no-]utf8] [--[no-]verify]
 	 [--[no-]3way] [--interactive] [--committer-date-is-author-date]
 	 [--ignore-date] [--ignore-space-change | --ignore-whitespace]
 	 [--whitespace=<action>] [-C<n>] [-p<n>] [--directory=<dir>]
 	 [--exclude=<path>] [--include=<path>] [--reject] [-q | --quiet]
-	 [--[no-]scissors] [-S[<keyid>]] [--patch-format=<format>]
+	 [--[no-]scissors] [-S[<key-id>]] [--patch-format=<format>]
 	 [--quoted-cr=<action>]
 	 [--empty=(stop|drop|keep)]
 	 [(<mbox> | <Maildir>)...]
-'git am' (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
+git am (--continue | --skip | --abort | --quit | --retry | --show-current-patch[=(diff|raw)] | --allow-empty)
 
 DESCRIPTION
 -----------
@@ -30,45 +30,45 @@ history without merges.
 
 OPTIONS
 -------
-(<mbox>|<Maildir>)...::
+`(<mbox>|<Maildir>)...`::
 	The list of mailbox files to read patches from. If you do not
 	supply this argument, the command reads from the standard input.
 	If you supply directories, they will be treated as Maildirs.
 
--s::
---signoff::
+`-s`::
+`--signoff`::
 	Add a `Signed-off-by` trailer to the commit message (see
 	linkgit:git-interpret-trailers[1]), using the committer identity
 	of yourself.  See the signoff option in linkgit:git-commit[1]
 	for more information.
 
--k::
---keep::
+`-k`::
+`--keep`::
 	Pass `-k` flag to linkgit:git-mailinfo[1].
 
---keep-non-patch::
+`--keep-non-patch`::
 	Pass `-b` flag to linkgit:git-mailinfo[1].
 
---keep-cr::
---no-keep-cr::
+`--keep-cr`::
+`--no-keep-cr`::
 	With `--keep-cr`, call linkgit:git-mailsplit[1]
 	with the same option, to prevent it from stripping CR at the end of
 	lines. `am.keepcr` configuration variable can be used to specify the
 	default behaviour.  `--no-keep-cr` is useful to override `am.keepcr`.
 
--c::
---scissors::
+`-c`::
+`--scissors`::
 	Remove everything in body before a scissors line (see
 	linkgit:git-mailinfo[1]). Can be activated by default using
 	the `mailinfo.scissors` configuration variable.
 
---no-scissors::
+`--no-scissors`::
 	Ignore scissors lines (see linkgit:git-mailinfo[1]).
 
---quoted-cr=<action>::
+`--quoted-cr=<action>`::
 	This flag will be passed down to linkgit:git-mailinfo[1].
 
---empty=(drop|keep|stop)::
+`--empty=(drop|keep|stop)`::
 	How to handle an e-mail message lacking a patch:
 +
 --
@@ -82,23 +82,23 @@ OPTIONS
 	session. This is the default behavior.
 --
 
--m::
---message-id::
+`-m`::
+`--message-id`::
 	Pass the `-m` flag to linkgit:git-mailinfo[1],
 	so that the `Message-ID` header is added to the commit message.
 	The `am.messageid` configuration variable can be used to specify
 	the default behaviour.
 
---no-message-id::
+`--no-message-id`::
 	Do not add the Message-ID header to the commit message.
 	`--no-message-id` is useful to override `am.messageid`.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Be quiet. Only print error messages.
 
--u::
---utf8::
+`-u`::
+`--utf8`::
 	Pass `-u` flag to linkgit:git-mailinfo[1].
 	The proposed commit log message taken from the e-mail
 	is re-coded into UTF-8 encoding (configuration variable
@@ -108,57 +108,57 @@ OPTIONS
 This was optional in prior versions of git, but now it is the
 default.   You can use `--no-utf8` to override this.
 
---no-utf8::
+`--no-utf8`::
 	Pass `-n` flag to linkgit:git-mailinfo[1].
 
--3::
---3way::
---no-3way::
+`-3`::
+`--3way`::
+`--no-3way`::
 	When the patch does not apply cleanly, fall back on
 	3-way merge if the patch records the identity of blobs
 	it is supposed to apply to and we have those blobs
 	available locally. `--no-3way` can be used to override
-	am.threeWay configuration variable. For more information,
-	see am.threeWay in linkgit:git-config[1].
+	`am.threeWay` configuration variable. For more information,
+	see `am.threeWay` in linkgit:git-config[1].
 
 include::rerere-options.adoc[]
 
---ignore-space-change::
---ignore-whitespace::
---whitespace=<action>::
--C<n>::
--p<n>::
---directory=<dir>::
---exclude=<path>::
---include=<path>::
---reject::
+`--ignore-space-change`::
+`--ignore-whitespace`::
+`--whitespace=<action>`::
+`-C<n>`::
+`-p<n>`::
+`--directory=<dir>`::
+`--exclude=<path>`::
+`--include=<path>`::
+`--reject`::
 	These flags are passed to the linkgit:git-apply[1] program that
 	applies the patch.
 +
-Valid <action> for the `--whitespace` option are:
+Valid _<action>_ for the `--whitespace` option are:
 `nowarn`, `warn`, `fix`, `error`, and `error-all`.
 
---patch-format::
+`--patch-format`::
 	By default the command will try to detect the patch format
 	automatically. This option allows the user to bypass the automatic
 	detection and specify the patch format that the patch(es) should be
 	interpreted as. Valid formats are mbox, mboxrd,
 	stgit, stgit-series, and hg.
 
--i::
---interactive::
+`-i`::
+`--interactive`::
 	Run interactively.
 
---verify::
--n::
---no-verify::
+`--verify`::
+`-n`::
+`--no-verify`::
 	Run the `pre-applypatch` and `applypatch-msg` hooks. This is the
 	default. Skip these hooks with `-n` or `--no-verify`. See also
 	linkgit:githooks[5].
 +
 Note that `post-applypatch` cannot be skipped.
 
---committer-date-is-author-date::
+`--committer-date-is-author-date`::
 	By default the command records the date from the e-mail
 	message as the commit author date, and uses the time of
 	commit creation as the committer date. This allows the
@@ -172,29 +172,29 @@ committer date when applying commits on top of a base which commit is
 older (in terms of the commit date) than the oldest patch you are
 applying.
 
---ignore-date::
+`--ignore-date`::
 	By default the command records the date from the e-mail
 	message as the commit author date, and uses the time of
 	commit creation as the committer date. This allows the
 	user to lie about the author date by using the same
 	value as the committer date.
 
---skip::
+`--skip`::
 	Skip the current patch.  This is only meaningful when
 	restarting an aborted patch.
 
--S[<keyid>]::
---gpg-sign[=<keyid>]::
---no-gpg-sign::
-	GPG-sign commits. The `keyid` argument is optional and
+`-S[<key-id>]`::
+`--gpg-sign[=<key-id>]`::
+`--no-gpg-sign`::
+	GPG-sign commits. The _<key-id>_ is optional and
 	defaults to the committer identity; if specified, it must be
 	stuck to the option without a space. `--no-gpg-sign` is useful to
 	countermand both `commit.gpgSign` configuration variable, and
 	earlier `--gpg-sign`.
 
---continue::
--r::
---resolved::
+`--continue`::
+`-r`::
+`--resolved`::
 	After a patch failure (e.g. attempting to apply
 	conflicting patch), the user has applied it by hand and
 	the index file stores the result of the application.
@@ -202,36 +202,36 @@ applying.
 	extracted from the e-mail message and the current index
 	file, and continue.
 
---resolvemsg=<msg>::
-	When a patch failure occurs, <msg> will be printed
+`--resolvemsg=<msg>`::
+	When a patch failure occurs, _<msg>_ will be printed
 	to the screen before exiting.  This overrides the
 	standard message informing you to use `--continue`
 	or `--skip` to handle the failure.  This is solely
 	for internal use between linkgit:git-rebase[1] and
 	linkgit:git-am[1].
 
---abort::
+`--abort`::
 	Restore the original branch and abort the patching operation.
 	Revert the contents of files involved in the am operation to their
 	pre-am state.
 
---quit::
-	Abort the patching operation but keep HEAD and the index
+`--quit`::
+	Abort the patching operation but keep `HEAD` and the index
 	untouched.
 
---retry::
+`--retry`::
 	Try to apply the last conflicting patch again. This is generally
 	only useful for passing extra options to the retry attempt
 	(e.g., `--3way`), since otherwise you'll just see the same
 	failure again.
 
---show-current-patch[=(diff|raw)]::
+`--show-current-patch[=(diff|raw)]`::
 	Show the message at which linkgit:git-am[1] has stopped due to
 	conflicts.  If `raw` is specified, show the raw contents of
 	the e-mail message; if `diff`, show the diff portion only.
 	Defaults to `raw`.
 
---allow-empty::
+`--allow-empty`::
 	After a patch failure on an input e-mail message lacking a patch,
 	create an empty commit with the contents of the e-mail message
 	as its log message.
@@ -278,11 +278,11 @@ operation is finished, so if you decide to start over from scratch,
 run `git am --abort` before running the command with mailbox
 names.
 
-Before any patches are applied, ORIG_HEAD is set to the tip of the
+Before any patches are applied, `ORIG_HEAD` is set to the tip of the
 current branch.  This is useful if you have problems with multiple
 commits, like running linkgit:git-am[1] on the wrong branch or an error
 in the commits that is more easily fixed by changing the mailbox (e.g.
-errors in the "From:" lines).
+errors in the `From:` lines).
 
 [[caveats]]
 CAVEATS
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH 2/5] doc: convert git-grep synopsis and options to new style
From: Jean-Noël Avila via GitGitGadget @ 2026-05-17 20:26 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila, Jean-Noël Avila
In-Reply-To: <pull.2117.git.1779049615.gitgitgadget@gmail.com>

From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Convert git-grep.adoc from [verse]/single-quote style to the modern
synopsis-block style:

- Replace [verse] with [synopsis] in SYNOPSIS block
- Change 'git grep' to git grep (no single quotes)
- Backtick-quote all OPTIONS terms
- Convert inline man page refs: grep(1) -> `grep`(1)
- Convert inline command refs: 'git diff' -> `git diff`
- Convert prose placeholders: <file> -> _<file>_

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Documentation/config/grep.adoc |  36 +++---
 Documentation/git-grep.adoc    | 196 ++++++++++++++++-----------------
 2 files changed, 116 insertions(+), 116 deletions(-)

diff --git a/Documentation/config/grep.adoc b/Documentation/config/grep.adoc
index 10041f27b0..83d4b76dd3 100644
--- a/Documentation/config/grep.adoc
+++ b/Documentation/config/grep.adoc
@@ -1,28 +1,28 @@
-grep.lineNumber::
-	If set to true, enable `-n` option by default.
+`grep.lineNumber`::
+	If set to `true`, enable `-n` option by default.
 
-grep.column::
-	If set to true, enable the `--column` option by default.
+`grep.column`::
+	If set to `true`, enable the `--column` option by default.
 
-grep.patternType::
-	Set the default matching behavior. Using a value of 'basic', 'extended',
-	'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
+`grep.patternType`::
+	Set the default matching behavior. Using a value of `basic`, `extended`,
+	`fixed`, or `perl` will enable the `--basic-regexp`, `--extended-regexp`,
 	`--fixed-strings`, or `--perl-regexp` option accordingly, while the
-	value 'default' will use the `grep.extendedRegexp` option to choose
-	between 'basic' and 'extended'.
+	value `default` will use the `grep.extendedRegexp` option to choose
+	between `basic` and `extended`.
 
-grep.extendedRegexp::
-	If set to true, enable `--extended-regexp` option by default. This
+`grep.extendedRegexp`::
+	If set to `true`, enable `--extended-regexp` option by default. This
 	option is ignored when the `grep.patternType` option is set to a value
-	other than 'default'.
+	other than `default`.
 
-grep.threads::
+`grep.threads`::
 	Number of grep worker threads to use. If unset (or set to 0), Git will
 	use as many threads as the number of logical cores available.
 
-grep.fullName::
-	If set to true, enable `--full-name` option by default.
+`grep.fullName`::
+	If set to `true`, enable `--full-name` option by default.
 
-grep.fallbackToNoIndex::
-	If set to true, fall back to `git grep --no-index` if `git grep`
-	is executed outside of a git repository.  Defaults to false.
+`grep.fallbackToNoIndex`::
+	If set to `true`, fall back to `git grep --no-index` if `git grep`
+	is executed outside of a git repository.  Defaults to `false`.
diff --git a/Documentation/git-grep.adoc b/Documentation/git-grep.adoc
index a548585d4c..19b3ade16d 100644
--- a/Documentation/git-grep.adoc
+++ b/Documentation/git-grep.adoc
@@ -8,8 +8,8 @@ git-grep - Print lines matching a pattern
 
 SYNOPSIS
 --------
-[verse]
-'git grep' [-a | --text] [-I] [--textconv] [-i | --ignore-case] [-w | --word-regexp]
+[synopsis]
+git grep [-a | --text] [-I] [--textconv] [-i | --ignore-case] [-w | --word-regexp]
 	   [-v | --invert-match] [-h|-H] [--full-name]
 	   [-E | --extended-regexp] [-G | --basic-regexp]
 	   [-P | --perl-regexp]
@@ -41,139 +41,139 @@ characters.  An empty string as search expression matches all lines.
 
 OPTIONS
 -------
---cached::
+`--cached`::
 	Instead of searching tracked files in the working tree, search
 	blobs registered in the index file.
 
---untracked::
+`--untracked`::
 	In addition to searching in the tracked files in the working
 	tree, search also in untracked files.
 
---no-index::
+`--no-index`::
 	Search files in the current directory that is not managed by Git,
 	or by ignoring that the current directory is managed by Git.  This
-	is rather similar to running the regular `grep(1)` utility with its
+	is rather similar to running the regular `grep`(1) utility with its
 	`-r` option specified, but with some additional benefits, such as
-	using pathspec patterns to limit paths;  see the 'pathspec' entry
+	using pathspec patterns to limit paths;  see the `pathspec` entry
 	in linkgit:gitglossary[7] for more information.
 +
 This option cannot be used together with `--cached` or `--untracked`.
 See also `grep.fallbackToNoIndex` in 'CONFIGURATION' below.
 
---no-exclude-standard::
+`--no-exclude-standard`::
 	Also search in ignored files by not honoring the `.gitignore`
 	mechanism. Only useful with `--untracked`.
 
---exclude-standard::
+`--exclude-standard`::
 	Do not pay attention to ignored files specified via the `.gitignore`
 	mechanism.  Only useful when searching files in the current directory
 	with `--no-index`.
 
---recurse-submodules::
+`--recurse-submodules`::
 	Recursively search in each submodule that is active and
 	checked out in the repository.  When used in combination with the
 	_<tree>_ option the prefix of all submodule output will be the name of
 	the parent project's _<tree>_ object.  This option cannot be used together
 	with `--untracked`, and it has no effect if `--no-index` is specified.
 
--a::
---text::
+`-a`::
+`--text`::
 	Process binary files as if they were text.
 
---textconv::
+`--textconv`::
 	Honor textconv filter settings.
 
---no-textconv::
+`--no-textconv`::
 	Do not honor textconv filter settings.
 	This is the default.
 
--i::
---ignore-case::
+`-i`::
+`--ignore-case`::
 	Ignore case differences between the patterns and the
 	files.
 
--I::
+`-I`::
 	Don't match the pattern in binary files.
 
---max-depth <depth>::
-	For each <pathspec> given on command line, descend at most <depth>
+`--max-depth <depth>`::
+	For each _<pathspec>_ given on command line, descend at most _<depth>_
 	levels of directories. A value of -1 means no limit.
-	This option is ignored if <pathspec> contains active wildcards.
+	This option is ignored if _<pathspec>_ contains active wildcards.
 	In other words if "a*" matches a directory named "a*",
-	"*" is matched literally so --max-depth is still effective.
+	"*" is matched literally so `--max-depth` is still effective.
 
--r::
---recursive::
+`-r`::
+`--recursive`::
 	Same as `--max-depth=-1`; this is the default.
 
---no-recursive::
+`--no-recursive`::
 	Same as `--max-depth=0`.
 
--w::
---word-regexp::
+`-w`::
+`--word-regexp`::
 	Match the pattern only at word boundary (either begin at the
 	beginning of a line, or preceded by a non-word character; end at
 	the end of a line or followed by a non-word character).
 
--v::
---invert-match::
+`-v`::
+`--invert-match`::
 	Select non-matching lines.
 
--h::
--H::
+`-h`::
+`-H`::
 	By default, the command shows the filename for each
 	match.  `-h` option is used to suppress this output.
 	`-H` is there for completeness and does not do anything
 	except it overrides `-h` given earlier on the command
 	line.
 
---full-name::
+`--full-name`::
 	When run from a subdirectory, the command usually
 	outputs paths relative to the current directory.  This
 	option forces paths to be output relative to the project
 	top directory.
 
--E::
---extended-regexp::
--G::
---basic-regexp::
+`-E`::
+`--extended-regexp`::
+`-G`::
+`--basic-regexp`::
 	Use POSIX extended/basic regexp for patterns.  Default
 	is to use basic regexp.
 
--P::
---perl-regexp::
+`-P`::
+`--perl-regexp`::
 	Use Perl-compatible regular expressions for patterns.
 +
 Support for these types of regular expressions is an optional
 compile-time dependency. If Git wasn't compiled with support for them
 providing this option will cause it to die.
 
--F::
---fixed-strings::
+`-F`::
+`--fixed-strings`::
 	Use fixed strings for patterns (don't interpret pattern
 	as a regex).
 
--n::
---line-number::
+`-n`::
+`--line-number`::
 	Prefix the line number to matching lines.
 
---column::
+`--column`::
 	Prefix the 1-indexed byte-offset of the first match from the start of the
 	matching line.
 
--l::
---files-with-matches::
---name-only::
--L::
---files-without-match::
+`-l`::
+`--files-with-matches`::
+`--name-only`::
+`-L`::
+`--files-without-match`::
 	Instead of showing every matched line, show only the
 	names of files that contain (or do not contain) matches.
-	For better compatibility with 'git diff', `--name-only` is a
+	For better compatibility with `git diff`, `--name-only` is a
 	synonym for `--files-with-matches`.
 
--O[<pager>]::
---open-files-in-pager[=<pager>]::
-	Open the matching files in the pager (not the output of 'grep').
+`-O[<pager>]`::
+`--open-files-in-pager[=<pager>]`::
+	Open the matching files in the pager (not the output of `grep`).
 	If the pager happens to be "less" or "vi", and the user
 	specified only one pattern, the first file is positioned at
 	the first match automatically. The `pager` argument is
@@ -181,65 +181,65 @@ providing this option will cause it to die.
 	without a space. If `pager` is unspecified, the default pager
 	will be used (see `core.pager` in linkgit:git-config[1]).
 
--z::
---null::
+`-z`::
+`--null`::
 	Use \0 as the delimiter for pathnames in the output, and print
 	them verbatim. Without this option, pathnames with "unusual"
 	characters are quoted as explained for the configuration
 	variable `core.quotePath` (see linkgit:git-config[1]).
 
--o::
---only-matching::
+`-o`::
+`--only-matching`::
 	Print only the matched (non-empty) parts of a matching line, with each such
 	part on a separate output line.
 
--c::
---count::
+`-c`::
+`--count`::
 	Instead of showing every matched line, show the number of
 	lines that match.
 
---color[=<when>]::
+`--color[=<when>]`::
 	Show colored matches.
-	The value must be always (the default), never, or auto.
+	The value must be `always` (the default), `never`, or `auto`.
 
---no-color::
+`--no-color`::
 	Turn off match highlighting, even when the configuration file
 	gives the default to color output.
 	Same as `--color=never`.
 
---break::
+`--break`::
 	Print an empty line between matches from different files.
 
---heading::
+`--heading`::
 	Show the filename above the matches in that file instead of
 	at the start of each shown line.
 
--p::
---show-function::
+`-p`::
+`--show-function`::
 	Show the preceding line that contains the function name of
 	the match, unless the matching line is a function name itself.
 	The name is determined in the same way as `git diff` works out
 	patch hunk headers (see 'Defining a custom hunk-header' in
 	linkgit:gitattributes[5]).
 
--<num>::
--C <num>::
---context <num>::
-	Show <num> leading and trailing lines, and place a line
+`-<num>`::
+`-C <num>`::
+`--context <num>`::
+	Show _<num>_ leading and trailing lines, and place a line
 	containing `--` between contiguous groups of matches.
 
--A <num>::
---after-context <num>::
-	Show <num> trailing lines, and place a line containing
+`-A <num>`::
+`--after-context <num>`::
+	Show _<num>_ trailing lines, and place a line containing
 	`--` between contiguous groups of matches.
 
--B <num>::
---before-context <num>::
-	Show <num> leading lines, and place a line containing
+`-B <num>`::
+`--before-context <num>`::
+	Show _<num>_ leading lines, and place a line containing
 	`--` between contiguous groups of matches.
 
--W::
---function-context::
+`-W`::
+`--function-context`::
 	Show the surrounding text from the previous line containing a
 	function name up to the one before the next function name,
 	effectively showing the whole function in which the match was
@@ -247,22 +247,22 @@ providing this option will cause it to die.
 	`git diff` works out patch hunk headers (see 'Defining a
 	custom hunk-header' in linkgit:gitattributes[5]).
 
--m <num>::
---max-count <num>::
+`-m <num>`::
+`--max-count <num>`::
 	Limit the amount of matches per file. When using the `-v` or
 	`--invert-match` option, the search stops after the specified
 	number of non-matches. A value of -1 will return unlimited
 	results (the default). A value of 0 will exit immediately with
 	a non-zero status.
 
---threads <num>::
-	Number of `grep` worker threads to use.  See 'NOTES ON THREADS'
+`--threads <num>`::
+	Number of `grep` worker threads to use.  See `NOTES ON THREADS`
 	and `grep.threads` in 'CONFIGURATION' for more information.
 
--f <file>::
-	Read patterns from <file>, one per line.
+`-f <file>`::
+	Read patterns from _<file>_, one per line.
 +
-Passing the pattern via <file> allows for providing a search pattern
+Passing the pattern via _<file>_ allows for providing a search pattern
 containing a \0.
 +
 Not all pattern types support patterns containing \0. Git will error
@@ -279,44 +279,44 @@ In future versions we may learn to support patterns containing \0 for
 more search backends, until then we'll die when the pattern type in
 question doesn't support them.
 
--e::
+`-e`::
 	The next parameter is the pattern. This option has to be
 	used for patterns starting with `-` and should be used in
 	scripts passing user input to grep.  Multiple patterns are
-	combined by 'or'.
+	combined by `or`.
 
---and::
---or::
---not::
-( ... )::
+`--and`::
+`--or`::
+`--not`::
+`( ... )`::
 	Specify how multiple patterns are combined using Boolean
 	expressions.  `--or` is the default operator.  `--and` has
 	higher precedence than `--or`.  `-e` has to be used for all
 	patterns.
 
---all-match::
+`--all-match`::
 	When giving multiple pattern expressions combined with `--or`,
 	this flag is specified to limit the match to files that
 	have lines to match all of them.
 
--q::
---quiet::
+`-q`::
+`--quiet`::
 	Do not output matched lines; instead, exit with status 0 when
 	there is a match and with non-zero status when there isn't.
 
-<tree>...::
+`<tree>...`::
 	Instead of searching tracked files in the working tree, search
 	blobs in the given trees.
 
-\--::
+`--`::
 	Signals the end of options; the rest of the parameters
-	are <pathspec> limiters.
+	are _<pathspec>_ limiters.
 
-<pathspec>...::
+`<pathspec>...`::
 	If given, limit the search to paths matching at least one pattern.
-	Both leading paths match and glob(7) patterns are supported.
+	Both leading paths match and `glob`(7) patterns are supported.
 +
-For more details about the <pathspec> syntax, see the 'pathspec' entry
+For more details about the _<pathspec>_ syntax, see the `pathspec` entry
 in linkgit:gitglossary[7].
 
 EXAMPLES
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH 1/5] doc: convert git-bisect to synopsis style
From: Jean-Noël Avila via GitGitGadget @ 2026-05-17 20:26 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila, Jean-Noël Avila
In-Reply-To: <pull.2117.git.1779049615.gitgitgadget@gmail.com>

From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>

Convert Documentation/git-bisect.adoc to the modern synopsis style.

- Replace [verse] with [synopsis] in the SYNOPSIS block
- Remove single quotes around command names in the synopsis
- Use backticks for inline commands, options, refs, and special values
- Apply [synopsis] attribute to in-body command-form code blocks
- Format OPTIONS entries with backtick-quoted terms and direct
- Add synopsis-style formatting to listing blocks
- Format man page references as `command`(N)

Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>
---
 Documentation/asciidoc.conf.in |  6 +++
 Documentation/git-bisect.adoc  | 80 ++++++++++++++++++----------------
 2 files changed, 48 insertions(+), 38 deletions(-)

diff --git a/Documentation/asciidoc.conf.in b/Documentation/asciidoc.conf.in
index 31b883a72c..93c63b284a 100644
--- a/Documentation/asciidoc.conf.in
+++ b/Documentation/asciidoc.conf.in
@@ -84,6 +84,9 @@ ifdef::doctype-manpage[]
 [blockdef-open]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
 
+[blockdef-listing]
+synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
+
 [paradef-default]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<phrase>\\0</phrase>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<literal>\\2</literal>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<emphasis>\\0</emphasis>!g'"
 endif::doctype-manpage[]
@@ -93,6 +96,9 @@ ifdef::backend-xhtml11[]
 [blockdef-open]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
 
+[blockdef-listing]
+synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
+
 [paradef-default]
 synopsis-style=template="verseparagraph",filter="sed 's!&#8230;\\(\\]\\|$\\)!<span>\\0</span>!g;s!\\([\\[ |()]\\|^\\|\\]\\|&gt;\\)\\([-=a-zA-Z0-9:+@,\\/_^\\$.\\\\\\*]\\+\\|&#8230;\\)!\\1<code>\\2</code>!g;s!&lt;[-a-zA-Z0-9.]\\+&gt;!<em>\\0</em>!g'"
 endif::backend-xhtml11[]
diff --git a/Documentation/git-bisect.adoc b/Documentation/git-bisect.adoc
index b0078dda0e..9a3b7b3cdb 100644
--- a/Documentation/git-bisect.adoc
+++ b/Documentation/git-bisect.adoc
@@ -8,20 +8,20 @@ git-bisect - Use binary search to find the commit that introduced a bug
 
 SYNOPSIS
 --------
-[verse]
-'git bisect' start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
-		   [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
-'git bisect' (bad|new|<term-new>) [<rev>]
-'git bisect' (good|old|<term-old>) [<rev>...]
-'git bisect' terms [--term-(good|old) | --term-(bad|new)]
-'git bisect' skip [(<rev>|<range>)...]
-'git bisect' next
-'git bisect' reset [<commit>]
-'git bisect' (visualize|view)
-'git bisect' replay <logfile>
-'git bisect' log
-'git bisect' run <cmd> [<arg>...]
-'git bisect' help
+[synopsis]
+git bisect start [--term-(bad|new)=<term-new> --term-(good|old)=<term-old>]
+		 [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<pathspec>...]
+git bisect (bad|new|<term-new>) [<rev>]
+git bisect (good|old|<term-old>) [<rev>...]
+git bisect terms [--term-(good|old) | --term-(bad|new)]
+git bisect skip [(<rev>|<range>)...]
+git bisect next
+git bisect reset [<commit>]
+git bisect (visualize|view)
+git bisect replay <logfile>
+git bisect log
+git bisect run <cmd> [<arg>...]
+git bisect help
 
 DESCRIPTION
 -----------
@@ -94,7 +94,7 @@ Bisect reset
 ~~~~~~~~~~~~
 
 After a bisect session, to clean up the bisection state and return to
-the original HEAD, issue the following command:
+the original `HEAD`, issue the following command:
 
 ------------------------------------------------
 $ git bisect reset
@@ -107,6 +107,7 @@ that, as it cleans up the old bisection state.)
 With an optional argument, you can return to a different commit
 instead:
 
+[synopsis]
 ------------------------------------------------
 $ git bisect reset <commit>
 ------------------------------------------------
@@ -143,12 +144,14 @@ To use "old" and "new" instead of "good" and bad, you must run `git
 bisect start` without commits as argument and then run the following
 commands to add the commits:
 
+[synopsis]
 ------------------------------------------------
 git bisect old [<rev>]
 ------------------------------------------------
 
 to indicate that a commit was before the sought change, or
 
+[synopsis]
 ------------------------------------------------
 git bisect new [<rev>...]
 ------------------------------------------------
@@ -157,6 +160,7 @@ to indicate that it was after.
 
 To get a reminder of the currently used terms, use
 
+[synopsis]
 ------------------------------------------------
 git bisect terms
 ------------------------------------------------
@@ -171,6 +175,7 @@ If you would like to use your own terms instead of "bad"/"good" or
 subcommands like `reset`, `start`, ...) by starting the
 bisection using
 
+[synopsis]
 ------------------------------------------------
 git bisect start --term-old <term-old> --term-new <term-new>
 ------------------------------------------------
@@ -194,7 +199,7 @@ of `git bisect good` and `git bisect bad` to mark commits.
 Bisect visualize/view
 ~~~~~~~~~~~~~~~~~~~~~
 
-To see the currently remaining suspects in 'gitk', issue the following
+To see the currently remaining suspects in `gitk`, issue the following
 command during the bisection process (the subcommand `view` can be used
 as an alternative to `visualize`):
 
@@ -203,12 +208,13 @@ $ git bisect visualize
 ------------
 
 Git detects a graphical environment through various environment variables:
-`DISPLAY`, which is set in X Window System environments on Unix systems.
-`SESSIONNAME`, which is set under Cygwin in interactive desktop sessions.
-`MSYSTEM`, which is set under Msys2 and Git for Windows.
-`SECURITYSESSIONID`, which may be set on macOS in interactive desktop sessions.
 
-If none of these environment variables is set, 'git log' is used instead.
+`DISPLAY`:: which is set in X Window System environments on Unix systems.
+`SESSIONNAME`:: which is set under Cygwin in interactive desktop sessions.
+`MSYSTEM`:: which is set under Msys2 and Git for Windows.
+`SECURITYSESSIONID`:: which may be set on macOS in interactive desktop sessions.
+
+If none of these environment variables is set, `git log` is used instead.
 You can also give command-line options such as `-p` and `--stat`.
 
 ------------
@@ -342,8 +348,8 @@ code between 1 and 127 (inclusive), except 125, if the current source
 code is bad/new.
 
 Any other exit code will abort the bisect process. It should be noted
-that a program that terminates via `exit(-1)` leaves $? = 255, (see the
-exit(3) manual page), as the value is chopped with `& 0377`.
+that a program that terminates via `exit(-1)` leaves `$?` = 255, (see the
+`exit`(3) manual page), as the value is chopped with `& 0377`.
 
 The special exit code 125 should be used when the current source code
 cannot be tested. If the script exits with this code, the current
@@ -355,12 +361,12 @@ details do not matter, as they are normal errors in the script, as far as
 `bisect run` is concerned).
 
 You may often find that during a bisect session you want to have
-temporary modifications (e.g. s/#define DEBUG 0/#define DEBUG 1/ in a
+temporary modifications (e.g. `s/#define DEBUG 0/#define DEBUG 1/` in a
 header file, or "revision that does not have this commit needs this
 patch applied to work around another problem this bisection is not
 interested in") applied to the revision being tested.
 
-To cope with such a situation, after the inner 'git bisect' finds the
+To cope with such a situation, after the inner `git bisect` finds the
 next revision to test, the script can apply the patch
 before compiling, run the real test, and afterwards decide if the
 revision (possibly with the needed patch) passed the test and then
@@ -370,20 +376,18 @@ determine the eventual outcome of the bisect session.
 
 OPTIONS
 -------
---no-checkout::
-+
-Do not checkout the new working tree at each iteration of the bisection
-process. Instead just update the reference named `BISECT_HEAD` to make
-it point to the commit that should be tested.
+`--no-checkout`::
+	Do not checkout the new working tree at each iteration of the bisection
+	process. Instead just update the reference named `BISECT_HEAD` to make
+	it point to the commit that should be tested.
 +
 This option may be useful when the test you would perform in each step
 does not require a checked out tree.
 +
 If the repository is bare, `--no-checkout` is assumed.
 
---first-parent::
-+
-Follow only the first parent commit upon seeing a merge commit.
+`--first-parent`::
+	Follow only the first parent commit upon seeing a merge commit.
 +
 In detecting regressions introduced through the merging of a branch, the merge
 commit will be identified as introduction of the bug and its ancestors will be
@@ -395,7 +399,7 @@ branch contained broken or non-buildable commits, but the merge itself was OK.
 EXAMPLES
 --------
 
-* Automatically bisect a broken build between v1.2 and HEAD:
+* Automatically bisect a broken build between v1.2 and `HEAD`:
 +
 ------------
 $ git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
@@ -403,7 +407,7 @@ $ git bisect run make                # "make" builds the app
 $ git bisect reset                   # quit the bisect session
 ------------
 
-* Automatically bisect a test failure between origin and HEAD:
+* Automatically bisect a test failure between origin and `HEAD`:
 +
 ------------
 $ git bisect start HEAD origin --    # HEAD is bad, origin is good
@@ -430,7 +434,7 @@ and `exit 1` otherwise.
 +
 It is safer if both `test.sh` and `check_test_case.sh` are
 outside the repository to prevent interactions between the bisect,
-make and test processes and the scripts.
+`make` and test processes and the scripts.
 
 * Automatically bisect with temporary modifications (hot-fix):
 +
@@ -491,9 +495,9 @@ $ git bisect run sh -c '
 $ git bisect reset                   # quit the bisect session
 ------------
 +
-In this case, when 'git bisect run' finishes, bisect/bad will refer to a commit that
+In this case, when `git bisect run` finishes, `bisect/bad` will refer to a commit that
 has at least one parent whose reachable graph is fully traversable in the sense
-required by 'git pack objects'.
+required by `git pack-objects`.
 
 * Look for a fix instead of a regression in the code
 +
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH 0/5] doc: convert another batch of files to synopsis style
From: Jean-Noël Avila via GitGitGadget @ 2026-05-17 20:26 UTC (permalink / raw)
  To: git; +Cc: Jean-Noël Avila

This time, 4 new conversions:

 * git-grep
 * git-am
 * git-apply
 * git-imap-send

This batch was an opportunity to test AI-helped conversion.

Jean-Noël Avila (5):
  doc: convert git-bisect to synopsis style
  doc: convert git-grep synopsis and options to new style
  doc: convert git-am synopsis and options to new style
  doc: convert git-apply synopsis and options to new style
  doc: convert git-imap-send synopsis and options to new style

 Documentation/asciidoc.conf.in                |   6 +
 Documentation/config/am.adoc                  |   6 +-
 Documentation/config/apply.adoc               |  17 +-
 Documentation/config/grep.adoc                |  36 ++--
 Documentation/config/imap.adoc                |  30 +--
 Documentation/format-patch-caveats.adoc       |   2 +-
 .../format-patch-end-of-commit-message.adoc   |   4 +-
 Documentation/git-am.adoc                     | 132 ++++++------
 Documentation/git-apply.adoc                  | 125 +++++------
 Documentation/git-bisect.adoc                 |  80 +++----
 Documentation/git-grep.adoc                   | 196 +++++++++---------
 Documentation/git-imap-send.adoc              |  24 +--
 12 files changed, 337 insertions(+), 321 deletions(-)


base-commit: 59ff4886a579f4bc91e976fe18590b9ae02c7a08
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2117%2Fjnavila%2Fbisect-synopsis-style-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2117/jnavila/bisect-synopsis-style-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2117
-- 
gitgitgadget

^ permalink raw reply

* Re: git rebase --continue segfault
From: Michael Montalbo @ 2026-05-17 19:42 UTC (permalink / raw)
  To: admin; +Cc: git

On Sun, 17 May 2026 12:19:50 +0300, Alex Naidenkov <admin@leshe4ka.ru> wrote:
>
> Hi, ive encountered on segfault when ran `git rebase --continue`.
> Hopefully this would help
>
> - i was in the middle of big rebase
>
> - entered pin for signing commit
> - segfault happened

Does `git fsck` show anything / also crash?

^ permalink raw reply

* [PATCH v3] refs/files: skip lock files during consistency checks
From: Karthik Nayak @ 2026-05-17 17:32 UTC (permalink / raw)
  To: git; +Cc: ps, gitster, Christian Couder, Karthik Nayak
In-Reply-To: <20260420-refs-fsck-skip-lock-files-v1-1-c2595e206a76@gmail.com>

Consistency checks in the files reference backend involve two steps:

1. Iterate over all entries within the 'refs/' directory and call
`files_fsck_ref()` on each.
2. Iterate over all root refs via `for_each_root_ref()` and call
`files_fsck_ref()` on each.

`files_fsck_ref()` then runs all fsck checks defined in
`fsck_refs_fn[]`. Step 2 goes through the refs API and only sees valid
refs, but step 1 iterates the directory directly and may also encounter
intermediate '*.lock' files.

Currently, `files_fsck_refs_name()`, one of the functions in
`fsck_refs_fn[]`, filters out lock files itself. The other function,
`files_fsck_refs_content()`, has no such check and would parse the lock
file. Any new function added to `fsck_refs_fn[]` would have the same
problem.

Move the filter up into `files_fsck_refs_dir()`, where the directory
iteration happens. Since step 2 cannot produce lock files, this is the
only site where the filter is needed, and individual checks no longer
have to re-implement it.

Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
Changes in v3:
- Fix grammar in the commit message.
- Link to v2: https://patch.msgid.link/20260422-refs-fsck-skip-lock-files-v2-1-9607571ae59a@gmail.com

Changes in v2:
- Modified the commit message to clarify the changes made and reasoning.
- Modify the comment in the code to be more accurate.
- Add another additional test for bare lock files.
- Link to v1: https://patch.msgid.link/20260420-refs-fsck-skip-lock-files-v1-1-c2595e206a76@gmail.com
---
 refs/files-backend.c     | 22 +++++++++++-----------
 t/t0602-reffiles-fsck.sh | 41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 11 deletions(-)

diff --git a/refs/files-backend.c b/refs/files-backend.c
index b3b0c25f84..1504a1e2f3 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -3864,22 +3864,12 @@ static int files_fsck_refs_content(struct ref_store *ref_store,
 static int files_fsck_refs_name(struct ref_store *ref_store UNUSED,
 				struct fsck_options *o,
 				const char *refname,
-				const char *path,
+				const char *path UNUSED,
 				int mode UNUSED)
 {
 	struct strbuf sb = STRBUF_INIT;
-	const char *filename;
 	int ret = 0;
 
-	filename = basename((char *) path);
-
-	/*
-	 * Ignore the files ending with ".lock" as they may be lock files
-	 * However, do not allow bare ".lock" files.
-	 */
-	if (filename[0] != '.' && ends_with(filename, ".lock"))
-		goto cleanup;
-
 	if (is_root_ref(refname))
 		goto cleanup;
 
@@ -3939,6 +3929,7 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
 	struct strbuf refname = STRBUF_INIT;
 	struct strbuf sb = STRBUF_INIT;
 	struct dir_iterator *iter;
+	const char *filename;
 	int iter_status;
 	int ret = 0;
 
@@ -3962,6 +3953,15 @@ static int files_fsck_refs_dir(struct ref_store *ref_store,
 			strbuf_addf(&refname, "worktrees/%s/", wt->id);
 		strbuf_addf(&refname, "refs/%s", iter->relative_path);
 
+		filename = basename((char *) iter->path.buf);
+
+		/*
+		 * Ignore the files ending with ".lock" as they may be lock files.
+		 * However, do not skip invalid refnames with '.lock' suffix.
+		 */
+		if (filename[0] != '.' && ends_with(filename, ".lock"))
+			continue;
+
 		if (files_fsck_ref(ref_store, o, refname.buf,
 				   iter->path.buf, iter->st.st_mode) < 0)
 			ret = -1;
diff --git a/t/t0602-reffiles-fsck.sh b/t/t0602-reffiles-fsck.sh
index 3c1f553b81..13259821a0 100755
--- a/t/t0602-reffiles-fsck.sh
+++ b/t/t0602-reffiles-fsck.sh
@@ -87,6 +87,47 @@ test_expect_success 'ref name should be checked' '
 	)
 '
 
+test_expect_success 'lock files should be ignored' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git commit --allow-empty -m initial &&
+		git checkout -b branch-1 &&
+
+		touch .git/refs/heads/branch-1.lock &&
+		git refs verify 2>err &&
+		test_must_be_empty err &&
+
+		echo "foobar" >.git/refs/heads/branch-2 &&
+		test_must_fail git refs verify 2>err &&
+		cat >expect <<-EOF &&
+		error: refs/heads/branch-2: badRefContent: foobar
+		EOF
+		test_cmp expect err
+	)
+'
+
+test_expect_success 'bare lock files should not be ignored' '
+	test_when_finished "rm -rf repo" &&
+	git init repo &&
+	(
+		cd repo &&
+		git commit --allow-empty -m initial &&
+		git checkout -b branch-1 &&
+
+		# invalid refname should be reported
+		cp .git/refs/heads/branch-1 .git/refs/heads/.branch-1.lock &&
+		# invalid refname and content should be reported
+		touch .git/refs/heads/.lock &&
+
+		test_must_fail git refs verify 2>err &&
+		test_grep "error: refs/heads/.branch-1.lock: badRefName: invalid refname format" err &&
+		test_grep "error: refs/heads/.lock: badRefName: invalid refname format" err &&
+		test_grep "error: refs/heads/.lock: badRefContent: " err
+	)
+'
+
 test_expect_success 'ref name check should be adapted into fsck messages' '
 	test_when_finished "rm -rf repo" &&
 	git init repo &&




^ permalink raw reply related

* Re: What's cooking in git.git (May 2026, #03)
From: Karthik Nayak @ 2026-05-17 17:20 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqv7cod2lo.fsf@gitster.g>

[-- Attachment #1: Type: text/plain, Size: 1348 bytes --]

Junio C Hamano <gitster@pobox.com> writes:

> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> Hello Junio,
>>
>> I've not been active on the list past few weeks, did we reach a
>> consensus about
>> 20260420-refs-fsck-skip-lock-files-v1-1-c2595e206a76@gmail.com ? Or was
>> it missed, I thought it was in a ready state, but happy to reiterate as
>> needed.
>>
>> Lore: https://lore.kernel.org/git/20260420-refs-fsck-skip-lock-files-v1-1-c2595e206a76@gmail.com/#t
>
> I've not been active on the list past few weeks, either, so please
> don't expect me to know anything that happened during my 3-week
> absense ;-)
>

I didn't know that :)

> My understanding of the status of that thread is that after
>
> https://lore.kernel.org/git/CAOLa=ZT1zE+MLeaYE_5jWmNzSvtTTBw3ZAopai+2Ei27kmYm2g@mail.gmail.com/
>
> that said you "Will add ... locally", we are all waiting for you to
> say either "after waiting for sufficient amount of time, there
> wasn't any other major change necessary, so I won't add it locally
> after all" or "we have waited for sufficient amount of time, so here
> is the hopefully final edition that includes what I added locally
> following Patrick's review".

I did mention that I would avoid re-rolling since the changes suggested
were not big enough to warrant a re-roll. But makes sense, I'll send in
a new version. Thanks

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 690 bytes --]

^ permalink raw reply

* Re: [PATCH] revision: use priority queue in limit_list()
From: René Scharfe @ 2026-05-17 16:50 UTC (permalink / raw)
  To: Derrick Stolee, Kristofer Karlsson via GitGitGadget, git
  Cc: Kristofer Karlsson
In-Reply-To: <7e5abff7-79c9-41c3-9cfa-2aaf0e69a6a8@gmail.com>

On 5/14/26 9:57 PM, Derrick Stolee wrote:
> 
> This is good. Is there any chance that you could demonstrate this with
> any commits in the Git repo? It does have some interesting behavior,
> especially around point releases that are independent from the 'master'
> branch and thus could have lopsided symmetric differences using well-
> established tag names.
Couldn't find cases where the patch avoids quadratic runtimes, but nice
speedups nonetheless:


Benchmark 1: ./git_main rev-list --bisect v2.0.1..v2.10.1
  Time (mean ± σ):     108.2 ms ±   1.3 ms    [User: 104.3 ms, System: 3.3 ms]
  Range (min … max):   106.3 ms … 110.4 ms    27 runs

Benchmark 2: ./git rev-list --bisect v2.0.1..v2.10.1
  Time (mean ± σ):      93.4 ms ±   0.7 ms    [User: 89.3 ms, System: 3.3 ms]
  Range (min … max):    92.2 ms …  94.7 ms    31 runs

Summary
  ./git rev-list --bisect v2.0.1..v2.10.1 ran
    1.16 ± 0.02 times faster than ./git_main rev-list --bisect v2.0.1..v2.10.1


Benchmark 1: ./git_main rev-list --bisect v2.0.1..v2.20.1
  Time (mean ± σ):     200.6 ms ±   1.8 ms    [User: 196.1 ms, System: 3.7 ms]
  Range (min … max):   197.3 ms … 203.2 ms    14 runs

Benchmark 2: ./git rev-list --bisect v2.0.1..v2.20.1
  Time (mean ± σ):     160.1 ms ±   0.9 ms    [User: 155.5 ms, System: 3.8 ms]
  Range (min … max):   158.7 ms … 161.7 ms    18 runs

Summary
  ./git rev-list --bisect v2.0.1..v2.20.1 ran
    1.25 ± 0.01 times faster than ./git_main rev-list --bisect v2.0.1..v2.20.1


Benchmark 1: ./git_main rev-list --bisect v2.0.1..v2.30.1
  Time (mean ± σ):     384.7 ms ±   2.1 ms    [User: 379.8 ms, System: 4.0 ms]
  Range (min … max):   382.5 ms … 390.0 ms    10 runs

Benchmark 2: ./git rev-list --bisect v2.0.1..v2.30.1
  Time (mean ± σ):     300.6 ms ±   0.6 ms    [User: 295.7 ms, System: 4.0 ms]
  Range (min … max):   299.9 ms … 301.7 ms    10 runs

Summary
  ./git rev-list --bisect v2.0.1..v2.30.1 ran
    1.28 ± 0.01 times faster than ./git_main rev-list --bisect v2.0.1..v2.30.1


Benchmark 1: ./git_main rev-list --bisect v2.0.1..v2.40.1
  Time (mean ± σ):     630.7 ms ±   4.2 ms    [User: 625.5 ms, System: 4.4 ms]
  Range (min … max):   625.0 ms … 637.4 ms    10 runs

Benchmark 2: ./git rev-list --bisect v2.0.1..v2.40.1
  Time (mean ± σ):     496.9 ms ±   1.6 ms    [User: 491.6 ms, System: 4.3 ms]
  Range (min … max):   494.2 ms … 499.5 ms    10 runs

Summary
  ./git rev-list --bisect v2.0.1..v2.40.1 ran
    1.27 ± 0.01 times faster than ./git_main rev-list --bisect v2.0.1..v2.40.1


Benchmark 1: ./git_main rev-list --bisect v2.0.1..v2.50.1
  Time (mean ± σ):     954.3 ms ±   7.9 ms    [User: 948.3 ms, System: 5.1 ms]
  Range (min … max):   943.0 ms … 965.6 ms    10 runs

Benchmark 2: ./git rev-list --bisect v2.0.1..v2.50.1
  Time (mean ± σ):     754.8 ms ±   4.4 ms    [User: 748.9 ms, System: 5.0 ms]
  Range (min … max):   750.4 ms … 765.6 ms    10 runs

Summary
  ./git rev-list --bisect v2.0.1..v2.50.1 ran
    1.26 ± 0.01 times faster than ./git_main rev-list --bisect v2.0.1..v2.50.1

René


^ permalink raw reply

* Re: [PATCH 02/18] setup: stop using `the_repository` in `is_inside_worktree()`
From: Tian Yuchen @ 2026-05-17 16:18 UTC (permalink / raw)
  To: Patrick Steinhardt, git; +Cc: Karthik Nayak, Elijah Newren, Junio C Hamano
In-Reply-To: <20260420-pks-setup-wo-the-repository-v1-2-f4a81c4988e8@pks.im>

On 4/20/26 16:22, Patrick Steinhardt wrote:

I've noticed something that does not entirely convince me:

> @@ -477,11 +476,13 @@ int is_inside_git_dir(void)
>  	return inside_git_dir;
>  }
>  
> -int is_inside_work_tree(void)
> +int is_inside_work_tree(struct repository *repo)
>  {
> -	if (inside_work_tree < 0)
> -		inside_work_tree = is_inside_dir(repo_get_work_tree(the_repository));
> -	return inside_work_tree;
> +	static struct strbuf buf = STRBUF_INIT;
> +	const char *worktree = repo_get_work_tree(repo);
> +	if (!worktree)
> +		return 0;
> +	return is_inside_dir(strbuf_realpath(&buf, worktree, 1));
>  }
> 

Is it correct to statically allocate memory for the cache here? Could 
this lead
to memory overwriting issues in a multi-threaded environment?

p.s. I'm not sure if this one is better:

> 
> int is_inside_work_tree(struct repository *repo)
> {
> 	struct strbuf buf = STRBUF_INIT;
> 	const char *worktree = repo_get_work_tree(repo);
> 	int ret;
> 
> 	if (!worktree)
> 		return 0;
> 
> 	ret = is_inside_dir(strbuf_realpath(&buf, worktree, 1));
> 	strbuf_release(&buf);
> 	return ret;
> }

Thanks! yuchen



^ permalink raw reply

* Re: [PATCH] revision: use priority queue in limit_list()
From: Kristofer Karlsson @ 2026-05-17 15:26 UTC (permalink / raw)
  To: Derrick Stolee
  Cc: Jeff King, Junio C Hamano, Kristofer Karlsson via GitGitGadget,
	git
In-Reply-To: <aad34ac2-4cd5-4c85-b8ff-14c0caaa1c7b@gmail.com>

Another note - I think I managed to apply the same change to
get_revision_1 too - speeding up a monorepo "git rev-list HEAD" by
3.3x so it seems like a reasonable thing to do.
This simplifies process_parents and also makes
commit_list_insert_by_date dead code.

The only caveat is that get_revision_1 starts to get messier and the
rev_info struct needs both a prio_queue and a linked list of commits -
and then flushing everything
from the list into the prio_queue when executing get_revision_1.

I don't want to pollute this patch with that change - should I start a
separate thread for it or just revisit this later?
(Perhaps I have too many optimization patches in flux already)

- Kristofer

On Fri, 15 May 2026 at 15:10, Derrick Stolee <stolee@gmail.com> wrote:
>
> On 5/15/2026 3:47 AM, Kristofer Karlsson wrote:
>
> > Unfortunately git.git's mostly-linear history doesn't
> > trigger the quadratic behavior (the queue stays narrow). Even with
> > 5,584 commits in the symmetric diff, `--left-right --count` finishes
> > in ~0.4s on git.git for both baseline and patched. A 50-pair
> > interleaved run shows no statistically significant difference:
> >
> >     git rev-list --left-right --count v2.47.1...v2.54.0 (git.git, 5,584 commits)
> >     50 interleaved paired runs:
> >
> >     baseline: mean 393ms, stdev 13ms, median 392ms
> >     patched:  mean 396ms, stdev 14ms, median 393ms
> >     paired t-test: +2.9ms, t=1.16, p>0.05 (not significant)
>
> Thanks for sharing these details! Consider my curiosity sated.
> > The existing t/perf tests don't cover this path. p0001 doesn't
> > use --left-right and p6010 is merge-base specific. I could add a
> > perf test, though it would need a merge-heavy test repo to show the
> > difference. Would a synthetic one (like p6010 does) be useful?
>
> I'm usually interested in encoding ways to repeatedly exercise
> these performance gains and preventing regression in the future.
> However, you've demonstrated that not all repositories have a
> data shape that reveals the performance problem.
>
> If you happen to find a publicly-available repository that shows
> this improvement, then documenting the performance benefits for
> that repo would be sufficient. I'm familiar with performance
> work that doesn't reveal its most important gains until working
> with private repositories at the proper scale, so don't sweat
> not having a public example.
>
> I don't think it's worth constructing a synthetic repo to
> demonstrate this issue. I was hoping that it would be low-
> hanging fruit to cover this in the perf test suite, but that
> does not seem to be the case.
>
> Thanks,
> -Stolee
>
>

^ permalink raw reply

* Re: [PATCH/RFC 1/5] replay: support replaying 2-parent merges
From: Johannes Schindelin @ 2026-05-17 14:33 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Johannes Schindelin via GitGitGadget, git, Elijah Newren,
	Patrick Steinhardt
In-Reply-To: <3dd21593-9945-4f9c-a9a0-f5c66504da49@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 30432 bytes --]

Hi Phillip,

On Fri, 8 May 2026, Phillip Wood wrote:

> On 08/05/2026 10:36, Phillip Wood wrote:
> > 
> > On 06/05/2026 23:43, Johannes Schindelin via GitGitGadget wrote:
> > >
> > > Elijah Newren spelled out a way to lift this limitation in his
> > > replay-design-notes [1] and prototyped it in a 2022
> > > work-in-progress sketch [2]. The idea is that a merge commit M on
> > > parents (P1, P2) records both an automatic merge of those parents
> > > AND any manual layer the author put on top of that automatic merge
> > > (textual conflict resolution and any semantic edit outside conflict
> > > markers). Replaying M onto rewritten parents (P1', P2') must
> > > preserve that manual layer, but the rewritten parents change the
> > > automatic merge, so a simple cherry-pick is wrong: the manual layer
> > > would be re-introduced on top of stale auto-merge text.
> > >
> > > What works instead is a three-way merge of three trees the existing
> > > infrastructure already knows how to compute. Let R be the recursive
> > > auto-merge of (P1, P2), O be M's actual tree and N be the recursive
> > > auto-merge of (P1', P2'). Then `git diff R O` is morally
> > > `git show --remerge-diff M`: it captures exactly what the author
> > > added on top of the automatic merge. A non-recursive 3-way merge
> > > with R as the merge base, O as side 1 and N as side 2 layers that
> > > manual contribution onto the freshly auto-merged rewritten parents
> > > (N) and produces the replayed tree.
> > 
> > So we cherry-pick the difference between the user's conflict resolution O
> > and the auto-merge M of the original parents onto the auto-merge N of the
> > replayed parents. If we have a topology that looks like
> > 
> >          |
> >          A
> >         /|\
> >        / B \
> >       E  |  D
> >          C /
> >          |/
> >          O
> > 
> > then running
> > 
> >      git replay --onto E --ancestry-path B..O
> > 
> > will replay C and O onto E. If the changes in E and D conflict but those
> > conflicts do not overlap with the conflicts in M that were resolved to
> > create O then the replayed version of O will contain conflict markers from
> > the conflicting changes in E and D. Because the previous conflict resolution
> > applies to N without conflicts we do not recognize that there are still
> > conflicts in N that need to be resolved.
> > 
> > Having realized this I went to look at Elijah's notes and they recognize
> > this possibility and suggest extending the xdiff merge code to detect when N
> > has conflicts that do not correspond to the conflicts in M. That sounds like
> > quite a lot of work. I've not put much effort into coming up with a
> > counterexample but think that because "git replay" and "git history" do not
> > yet allow the commits in the merged branches to be edited we may be able to
> > safely use the implementation proposed in this series if both merge parents
> > have been rebased (or we might want all the merge bases of the new merge to
> > be a descendants of "--onto"). In the example above if both the parents were
> > rebased onto E then any new conflicts would happen when picking D rather
> > than when recreating the merge.
> 
> One further thought - if only one of the parents has been rebased (i.e. we're
> replaying O with parents P1' and P2) then can we just cherry-pick the merge -
> instead of merging P1' and P2, use P1 as the merge-base with O and P1' as the
> merge heads?

That's a really good idea! That should _especially_ work well for the
conflict markers in case of conflicts.

Ciao,
Johannes

> 
> Thanks
> 
> Phillip
> 
> > Thanks
> > 
> > Phillip
> > 
> > > Implement `pick_merge_commit()` along those lines and dispatch to it
> > > from `replay_revisions()` when the commit being replayed has exactly
> > > two parents. Two specific points (learned the hard way) keep
> > > non-trivial cases working where the WIP sketch [2] bailed out.
> > > First, R and N use identical `merge_options.branch1` and `branch2`
> > > labels ("ours"/"theirs"). When the original parents conflicted on a
> > > region of a file, both R and N produce textually identical conflict
> > > markers; the outer non-recursive merge then sees N == R in that
> > > region and the user's manual resolution from O wins cleanly. Without
> > > this, the conflict-marker text would differ between R and N (because
> > > the inner merges would label the conflicts differently), and the
> > > outer merge would itself be unclean even when the user did supply a
> > > clean resolution. Second, an unclean inner merge
> > > (`result.clean == 0`) is _not_ fatal: the tree merge-ort produces in
> > > that case still has well-defined contents (with conflict markers in
> > > the conflicted files) and is a valid input to the outer
> > > non-recursive merge. Only a real error (`< 0`) propagates as
> > > failure.
> > >
> > > The replay propagates the textual diffs the user actually made in M;
> > > it does _not_ extrapolate symbol-level intent. If rewriting the
> > > parents pulls in genuinely new content (for example, a brand-new
> > > caller of a function that the merge renamed), that new content stays
> > > as the rewritten parents have it. Symbol-aware refactoring is out of
> > > scope here, just as it is for plain rebase.
> > >
> > > Octopus merges (more than two parents) and revert-of-merge are not
> > > supported and are surfaced as explicit errors at the dispatch point.
> > > The "split" sub-command of `git history` continues to refuse when
> > > the targeted commit is itself a merge: split semantics do not apply
> > > to merges. The pre-walk gate in `builtin/history.c` that previously
> > > rejected any merge in the rewrite path now only rejects octopus
> > > merges; rename it accordingly.
> > >
> > > A small refactor in `create_commit()` makes the merge case possible:
> > > the helper now takes a `struct commit_list *parents` rather than a
> > > single parent pointer and takes ownership of the list. The single
> > > existing caller in `pick_regular_commit()` builds and passes a
> > > one-element list; the new `pick_merge_commit()` builds a two-element
> > > list, with the order of the `from` and `merge` parents preserved.
> > >
> > > Update the negative expectations in t3451, t3452 and t3650 that were
> > > asserting the now-retired "not supported yet" message, replacing
> > > them with positive coverage where it fits. Octopus rejection and
> > > revert-of-merge rejection are covered by new positive tests in
> > > t3650. A dedicated test script with merge-replay scenarios driven by
> > > a new test-tool fixture builder will follow in a subsequent commit.
> > >
> > > [1] https://github.com/newren/git/blob/replay/replay-design-notes.txt
> > > [2] https://github.com/newren/git/
> > > commit/4c45e8955ef9bf7d01fd15d9106b3bdb8ea91b45
> > >
> > > Helped-by: Elijah Newren <newren@gmail.com>
> > > Assisted-by: Claude Opus 4.7
> > > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> > > ---
> > >   builtin/history.c         |  16 ++-
> > >   replay.c                  | 209 ++++++++++++++++++++++++++++++++++++--
> > >   t/t3451-history-reword.sh |  21 ++--
> > >   t/t3452-history-split.sh  |   6 +-
> > >   t/t3650-replay-basics.sh  |  46 ++++++++-
> > >   5 files changed, 269 insertions(+), 29 deletions(-)
> > >
> > > diff --git a/builtin/history.c b/builtin/history.c
> > > index 9526938085..00097b2226 100644
> > > --- a/builtin/history.c
> > > +++ b/builtin/history.c
> > > @@ -195,15 +195,15 @@ static int parse_ref_action(const struct option
> > > *opt, const char *value, int uns
> > >       return 0;
> > >   }
> > > -static int revwalk_contains_merges(struct repository *repo,
> > > -                   const struct strvec *revwalk_args)
> > > +static int revwalk_contains_octopus_merges(struct repository *repo,
> > > +                       const struct strvec *revwalk_args)
> > >   {
> > >       struct strvec args = STRVEC_INIT;
> > >       struct rev_info revs;
> > >       int ret;
> > >       strvec_pushv(&args, revwalk_args->v);
> > > -    strvec_push(&args, "--min-parents=2");
> > > +    strvec_push(&args, "--min-parents=3");
> > >       repo_init_revisions(repo, &revs, NULL);
> > > @@ -217,7 +217,7 @@ static int revwalk_contains_merges(struct repository
> > > *repo,
> > >       }
> > >       if (get_revision(&revs)) {
> > > -        ret = error(_("replaying merge commits is not supported yet!"));
> > > +        ret = error(_("replaying octopus merges is not supported"));
> > >           goto out;
> > >       }
> > > @@ -289,7 +289,7 @@ static int setup_revwalk(struct repository *repo,
> > >           strvec_push(&args, "HEAD");
> > >       }
> > > -    ret = revwalk_contains_merges(repo, &args);
> > > +    ret = revwalk_contains_octopus_merges(repo, &args);
> > >       if (ret < 0)
> > >           goto out;
> > > @@ -482,6 +482,9 @@ static int cmd_history_reword(int argc,
> > >       if (ret < 0) {
> > >           ret = error(_("failed replaying descendants"));
> > >           goto out;
> > > +    } else if (ret) {
> > > +        ret = error(_("conflict during replay; some descendants were not
> > > rewritten"));
> > > +        goto out;
> > >       }
> > >       ret = 0;
> > > @@ -721,6 +724,9 @@ static int cmd_history_split(int argc,
> > >       if (ret < 0) {
> > >           ret = error(_("failed replaying descendants"));
> > >           goto out;
> > > +    } else if (ret) {
> > > +        ret = error(_("conflict during replay; some descendants were not
> > > rewritten"));
> > > +        goto out;
> > >       }
> > >       ret = 0;
> > > diff --git a/replay.c b/replay.c
> > > index f96f1f6551..3dbce095f9 100644
> > > --- a/replay.c
> > > +++ b/replay.c
> > > @@ -1,6 +1,7 @@
> > >   #define USE_THE_REPOSITORY_VARIABLE
> > >   #include "git-compat-util.h"
> > > +#include "commit-reach.h"
> > >   #include "environment.h"
> > >   #include "hex.h"
> > >   #include "merge-ort.h"
> > > @@ -77,15 +78,21 @@ static void generate_revert_message(struct strbuf
> > > *msg,
> > >       repo_unuse_commit_buffer(repo, commit, message);
> > >   }
> > > +/*
> > > + * Build a new commit with the given tree and parent list, copying
> > > author,
> > > + * extra headers and (for pick mode) the commit message from `based_on`.
> > > + *
> > > + * Takes ownership of `parents`: it will be freed before returning, even
> > > on
> > > + * error. Parent order is preserved as supplied by the caller.
> > > + */
> > >   static struct commit *create_commit(struct repository *repo,
> > >                       struct tree *tree,
> > >                       struct commit *based_on,
> > > -                    struct commit *parent,
> > > +                    struct commit_list *parents,
> > >                       enum replay_mode mode)
> > >   {
> > >       struct object_id ret;
> > >       struct object *obj = NULL;
> > > -    struct commit_list *parents = NULL;
> > >       char *author = NULL;
> > >       char *sign_commit = NULL; /* FIXME: cli users might want to sign
> > > again */
> > >       struct commit_extra_header *extra = NULL;
> > > @@ -96,7 +103,6 @@ static struct commit *create_commit(struct repository
> > > *repo,
> > >       const char *orig_message = NULL;
> > >       const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
> > > -    commit_list_insert(parent, &parents);
> > >       extra = read_commit_extra_headers(based_on, exclude_gpgsig);
> > >       if (mode == REPLAY_MODE_REVERT) {
> > >           generate_revert_message(&msg, based_on, repo);
> > > @@ -273,6 +279,7 @@ static struct commit *pick_regular_commit(struct
> > > repository *repo,
> > >   {
> > >       struct commit *base, *replayed_base;
> > >       struct tree *pickme_tree, *base_tree, *replayed_base_tree;
> > > +    struct commit_list *parents = NULL;
> > >       if (pickme->parents) {
> > >           base = pickme->parents->item;
> > > @@ -327,7 +334,143 @@ static struct commit *pick_regular_commit(struct
> > > repository *repo,
> > >       if (oideq(&replayed_base_tree->object.oid, &result->tree- 
> > > >object.oid) &&
> > >           !oideq(&pickme_tree->object.oid, &base_tree->object.oid))
> > >           return replayed_base;
> > > -    return create_commit(repo, result->tree, pickme, replayed_base,
> > > mode);
> > > +    commit_list_insert(replayed_base, &parents);
> > > +    return create_commit(repo, result->tree, pickme, parents, mode);
> > > +}
> > > +
> > > +/*
> > > + * Replay a 2-parent merge commit by composing three calls into
> > > merge-ort:
> > > + *
> > > + *   R = recursive merge of pickme's two original parents (auto- remerge
> > > of
> > > + *       the original merge, accepting any conflicts)
> > > + *   N = recursive merge of the (possibly rewritten) parents
> > > + *   O = pickme's tree (the user's actual merge, including any manual
> > > + *       resolutions)
> > > + *
> > > + * The picked tree comes from a non-recursive merge using R as the base,
> > > + * O as side1 and N as side2. `git diff R O` is morally `git show
> > > + * --remerge-diff $oldmerge`, so this layers the user's original manual
> > > + * resolution on top of the freshly auto-merged rewritten parents (see
> > > + * `replay-design-notes.txt` on the `replay` branch of newren/git).
> > > + *
> > > + * If the outer 3-way merge is unclean, propagate the conflict status to
> > > + * the caller via `result->clean = 0` and return NULL. The two inner
> > > + * merges (R and N) being unclean is _not_ fatal: the conflict-markered
> > > + * trees they produce are valid inputs to the outer merge, and using
> > > + * identical labels for both inner merges keeps the marker text
> > > + * byte-equal between R and N so the user's resolution recorded in O
> > > + * collapses the conflict cleanly there. Octopus merges (more than two
> > > + * parents) and revert-of-merge are rejected by the caller before this
> > > + * function is invoked.
> > > + */
> > > +static struct commit *pick_merge_commit(struct repository *repo,
> > > +                    struct commit *pickme,
> > > +                    kh_oid_map_t *replayed_commits,
> > > +                    struct merge_options *merge_opt,
> > > +                    struct merge_result *result)
> > > +{
> > > +    struct commit *parent1, *parent2;
> > > +    struct commit *replayed_par1, *replayed_par2;
> > > +    struct tree *pickme_tree;
> > > +    struct merge_options remerge_opt = { 0 };
> > > +    struct merge_options new_merge_opt = { 0 };
> > > +    struct merge_result remerge_res = { 0 };
> > > +    struct merge_result new_merge_res = { 0 };
> > > +    struct commit_list *parent_bases = NULL;
> > > +    struct commit_list *replayed_bases = NULL;
> > > +    struct commit_list *parents;
> > > +    struct commit *picked = NULL;
> > > +    char *ancestor_name = NULL;
> > > +
> > > +    parent1 = pickme->parents->item;
> > > +    parent2 = pickme->parents->next->item;
> > > +
> > > +    /*
> > > +     * Map the merge's parents to their replayed counterparts. With the
> > > +     * boundary commits pre-seeded into `replayed_commits`, every parent
> > > +     * either has an explicit mapping (rewritten or boundary -> onto) or
> > > +     * sits outside the rewrite range entirely; the latter must stay at
> > > +     * the original parent commit, so use `parent` itself as the fallback
> > > +     * for both sides.
> > > +     */
> > > +    replayed_par1 = mapped_commit(replayed_commits, parent1, parent1);
> > > +    replayed_par2 = mapped_commit(replayed_commits, parent2, parent2);
> > > +
> > > +    /*
> > > +     * R: auto-remerge of the original parents.
> > > +     *
> > > +     * Use the same branch labels for the inner merges that compute R
> > > +     * and N so conflict markers (if any) are textually identical
> > > +     * between the two; the outer non-recursive merge can then collapse
> > > +     * the manual resolution from O against them.
> > > +     */
> > > +    init_basic_merge_options(&remerge_opt, repo);
> > > +    remerge_opt.show_rename_progress = 0;
> > > +    remerge_opt.branch1 = "ours";
> > > +    remerge_opt.branch2 = "theirs";
> > > +    if (repo_get_merge_bases(repo, parent1, parent2, &parent_bases) < 0)
> > > {
> > > +        result->clean = -1;
> > > +        goto out;
> > > +    }
> > > +    merge_incore_recursive(&remerge_opt, parent_bases,
> > > +                   parent1, parent2, &remerge_res);
> > > +    parent_bases = NULL; /* consumed by merge_incore_recursive */
> > > +    if (remerge_res.clean < 0) {
> > > +        result->clean = remerge_res.clean;
> > > +        goto out;
> > > +    }
> > > +
> > > +    /* N: fresh merge of the (possibly rewritten) parents. */
> > > +    init_basic_merge_options(&new_merge_opt, repo);
> > > +    new_merge_opt.show_rename_progress = 0;
> > > +    new_merge_opt.branch1 = "ours";
> > > +    new_merge_opt.branch2 = "theirs";
> > > +    if (repo_get_merge_bases(repo, replayed_par1, replayed_par2,
> > > +                 &replayed_bases) < 0) {
> > > +        result->clean = -1;
> > > +        goto out;
> > > +    }
> > > +    merge_incore_recursive(&new_merge_opt, replayed_bases,
> > > +                   replayed_par1, replayed_par2, &new_merge_res);
> > > +    replayed_bases = NULL; /* consumed by merge_incore_recursive */
> > > +    if (new_merge_res.clean < 0) {
> > > +        result->clean = new_merge_res.clean;
> > > +        goto out;
> > > +    }
> > > +
> > > +    /*
> > > +     * Outer non-recursive merge: base=R, side1=O (pickme), side2=N.
> > > +     */
> > > +    pickme_tree = repo_get_commit_tree(repo, pickme);
> > > +    ancestor_name = xstrfmt("auto-remerge of %s",
> > > +                oid_to_hex(&pickme->object.oid));
> > > +    merge_opt->ancestor = ancestor_name;
> > > +    merge_opt->branch1 = short_commit_name(repo, pickme);
> > > +    merge_opt->branch2 = "merge of replayed parents";
> > > +    merge_incore_nonrecursive(merge_opt,
> > > +                  remerge_res.tree,
> > > +                  pickme_tree,
> > > +                  new_merge_res.tree,
> > > +                  result);
> > > +    merge_opt->ancestor = NULL;
> > > +    merge_opt->branch1 = NULL;
> > > +    merge_opt->branch2 = NULL;
> > > +    if (!result->clean)
> > > +        goto out;
> > > +
> > > +    parents = NULL;
> > > +    commit_list_insert(replayed_par2, &parents);
> > > +    commit_list_insert(replayed_par1, &parents);
> > > +    picked = create_commit(repo, result->tree, pickme, parents,
> > > +                   REPLAY_MODE_PICK);
> > > +
> > > +out:
> > > +    free(ancestor_name);
> > > +    free_commit_list(parent_bases);
> > > +    free_commit_list(replayed_bases);
> > > +    merge_finalize(&remerge_opt, &remerge_res);
> > > +    merge_finalize(&new_merge_opt, &new_merge_res);
> > > +    return picked;
> > >   }
> > >   void replay_result_release(struct replay_result *result)
> > > @@ -407,17 +550,63 @@ int replay_revisions(struct rev_info *revs,
> > >       merge_opt.show_rename_progress = 0;
> > >       last_commit = onto;
> > >       replayed_commits = kh_init_oid_map();
> > > +
> > > +    /*
> > > +     * Seed the rewritten-commit map with each negative-side ("BOTTOM")
> > > +     * cmdline entry pointing at `onto`. This matters for merge replay:
> > > +     * a 2-parent merge whose first parent is the boundary (e.g. the
> > > +     * commit being reworded) must replay onto the rewritten boundary,
> > > +     * yet pick_merge_commit uses a self fallback so the second parent
> > > +     * (a side branch outside the rewrite range) is preserved as-is.
> > > +     * Pre-seeding the boundary disambiguates the two: in the map ->
> > > +     * rewritten, missing -> kept as-is.
> > > +     *
> > > +     * Only do this for the pick path; revert mode chains reverts
> > > +     * through last_commit and a pre-seeded boundary would short-circuit
> > > +     * that chain.
> > > +     */
> > > +    if (mode == REPLAY_MODE_PICK) {
> > > +        for (size_t i = 0; i < revs->cmdline.nr; i++) {
> > > +            struct rev_cmdline_entry *e = &revs->cmdline.rev[i];
> > > +            struct commit *boundary;
> > > +            khint_t pos;
> > > +            int hr;
> > > +
> > > +            if (!(e->flags & BOTTOM))
> > > +                continue;
> > > +            boundary = lookup_commit_reference_gently(revs->repo,
> > > +                                  &e->item->oid, 1);
> > > +            if (!boundary)
> > > +                continue;
> > > +            pos = kh_put_oid_map(replayed_commits,
> > > +                         boundary->object.oid, &hr);
> > > +            if (hr != 0)
> > > +                kh_value(replayed_commits, pos) = onto;
> > > +        }
> > > +    }
> > > +
> > >       while ((commit = get_revision(revs))) {
> > >           const struct name_decoration *decoration;
> > >           khint_t pos;
> > >           int hr;
> > > -        if (commit->parents && commit->parents->next)
> > > -            die(_("replaying merge commits is not supported yet!"));
> > > -
> > > -        last_commit = pick_regular_commit(revs->repo, commit,
> > > replayed_commits,
> > > -                          mode == REPLAY_MODE_REVERT ? last_commit :
> > > onto,
> > > -                          &merge_opt, &result, mode);
> > > +        if (commit->parents && commit->parents->next) {
> > > +            if (commit->parents->next->next) {
> > > +                ret = error(_("replaying octopus merges is not
> > > supported"));
> > > +                goto out;
> > > +            }
> > > +            if (mode == REPLAY_MODE_REVERT) {
> > > +                ret = error(_("reverting merge commits is not
> > > supported"));
> > > +                goto out;
> > > +            }
> > > +            last_commit = pick_merge_commit(revs->repo, commit,
> > > +                            replayed_commits,
> > > +                            &merge_opt, &result);
> > > +        } else {
> > > +            last_commit = pick_regular_commit(revs->repo, commit,
> > > replayed_commits,
> > > +                              mode == REPLAY_MODE_REVERT ? last_commit :
> > > onto,
> > > +                              &merge_opt, &result, mode);
> > > +        }
> > >           if (!last_commit)
> > >               break;
> > > diff --git a/t/t3451-history-reword.sh b/t/t3451-history-reword.sh
> > > index de7b357685..d103f866a2 100755
> > > --- a/t/t3451-history-reword.sh
> > > +++ b/t/t3451-history-reword.sh
> > > @@ -201,12 +201,21 @@ test_expect_success 'can reword a merge commit' '
> > >           git switch - &&
> > >           git merge theirs &&
> > > -        # It is not possible to replay merge commits embedded in the
> > > -        # history (yet).
> > > -        test_must_fail git -c core.editor=false history reword HEAD~
> > > 2>err &&
> > > -        test_grep "replaying merge commits is not supported yet" err &&
> > > +        # Reword a non-merge commit whose descendants include the
> > > +        # merge: replay carries the merge through.
> > > +        reword_with_message HEAD~ <<-EOF &&
> > > +        ours reworded
> > > +        EOF
> > > +        expect_graph <<-EOF &&
> > > +        *   Merge tag ${SQ}theirs${SQ}
> > > +        |\\
> > > +        | * theirs
> > > +        * | ours reworded
> > > +        |/
> > > +        * base
> > > +        EOF
> > > -        # But it is possible to reword a merge commit directly.
> > > +        # And reword a merge commit directly.
> > >           reword_with_message HEAD <<-EOF &&
> > >           Reworded merge commit
> > >           EOF
> > > @@ -214,7 +223,7 @@ test_expect_success 'can reword a merge commit' '
> > >           *   Reworded merge commit
> > >           |\
> > >           | * theirs
> > > -        * | ours
> > > +        * | ours reworded
> > >           |/
> > >           * base
> > >           EOF
> > > diff --git a/t/t3452-history-split.sh b/t/t3452-history-split.sh
> > > index 8ed0cebb50..ad6309f98b 100755
> > > --- a/t/t3452-history-split.sh
> > > +++ b/t/t3452-history-split.sh
> > > @@ -36,7 +36,7 @@ expect_tree_entries () {
> > >       test_cmp expect actual
> > >   }
> > > -test_expect_success 'refuses to work with merge commits' '
> > > +test_expect_success 'refuses to split a merge commit' '
> > >       test_when_finished "rm -rf repo" &&
> > >       git init repo &&
> > >       (
> > > @@ -49,9 +49,7 @@ test_expect_success 'refuses to work with merge commits'
> > > '
> > >           git switch - &&
> > >           git merge theirs &&
> > >           test_must_fail git history split HEAD 2>err &&
> > > -        test_grep "cannot split up merge commit" err &&
> > > -        test_must_fail git history split HEAD~ 2>err &&
> > > -        test_grep "replaying merge commits is not supported yet" err
> > > +        test_grep "cannot split up merge commit" err
> > >       )
> > >   '
> > > diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
> > > index 3353bc4a4d..368b1b0f9a 100755
> > > --- a/t/t3650-replay-basics.sh
> > > +++ b/t/t3650-replay-basics.sh
> > > @@ -103,10 +103,48 @@ test_expect_success 'cannot advance target ...
> > > ordering would be ill-defined' '
> > >       test_cmp expect actual
> > >   '
> > > -test_expect_success 'replaying merge commits is not supported yet' '
> > > -    echo "fatal: replaying merge commits is not supported yet!" 
> > > >expect &&
> > > -    test_must_fail git replay --advance=main main..topic-with-merge
> > > 2>actual &&
> > > -    test_cmp expect actual
> > > +test_expect_success 'using replay to rebase a 2-parent merge' '
> > > +    # main..topic-with-merge contains a 2-parent merge (P) introduced
> > > +    # via test_merge. Use --ref-action=print so this test does not
> > > +    # mutate state for subsequent tests in this file.
> > > +    git replay --ref-action=print --onto main main..topic-with-merge 
> > > >result &&
> > > +    test_line_count = 1 result &&
> > > +
> > > +    new_tip=$(cut -f 3 -d " " result) &&
> > > +
> > > +    # Result is still a 2-parent merge.
> > > +    git cat-file -p $new_tip >cat &&
> > > +    grep -c "^parent " cat >count &&
> > > +    echo 2 >expect &&
> > > +    test_cmp expect count &&
> > > +
> > > +    # Merge subject is preserved.
> > > +    echo P >expect &&
> > > +    git log -1 --format=%s $new_tip >actual &&
> > > +    test_cmp expect actual &&
> > > +
> > > +    # The replayed merge sits on top of main: walking back via the
> > > +    # first-parent chain reaches main.
> > > +    git merge-base --is-ancestor main $new_tip
> > > +'
> > > +
> > > +test_expect_success 'replaying an octopus merge is rejected' '
> > > +    # Build an octopus side-branch so the rest of the test state stays
> > > +    # untouched.
> > > +    test_when_finished "git update-ref -d refs/heads/octopus-tip" &&
> > > +    octopus_tip=$(git commit-tree -p topic4 -p topic1 -p topic3 \
> > > +        -m "octopus" $(git rev-parse topic4^{tree})) &&
> > > +    git update-ref refs/heads/octopus-tip "$octopus_tip" &&
> > > +
> > > +    test_must_fail git replay --ref-action=print --onto main \
> > > +        topic4..octopus-tip 2>actual &&
> > > +    test_grep "octopus merges" actual
> > > +'
> > > +
> > > +test_expect_success 'reverting a merge commit is rejected' '
> > > +    test_must_fail git replay --ref-action=print --revert=topic-with-
> > > merge \
> > > +        topic4..topic-with-merge 2>actual &&
> > > +    test_grep "reverting merge commits" actual
> > >   '
> > >   test_expect_success 'using replay to rebase two branches, one on top of
> > > other' '
> > 
> 
> 
> 

^ permalink raw reply

* Re: [PATCH/RFC 1/5] replay: support replaying 2-parent merges
From: Johannes Schindelin @ 2026-05-17 14:32 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Johannes Schindelin via GitGitGadget, git, Elijah Newren,
	Patrick Steinhardt
In-Reply-To: <72901ee2-1212-46cd-b752-f451cce6e1ff@gmail.com>

Hi Phillip,

On Fri, 8 May 2026, Phillip Wood wrote:

> On 06/05/2026 23:43, Johannes Schindelin via GitGitGadget wrote:
> > 
> > Elijah Newren spelled out a way to lift this limitation in his
> > replay-design-notes [1] and prototyped it in a 2022
> > work-in-progress sketch [2]. The idea is that a merge commit M on
> > parents (P1, P2) records both an automatic merge of those parents
> > AND any manual layer the author put on top of that automatic merge
> > (textual conflict resolution and any semantic edit outside conflict
> > markers). Replaying M onto rewritten parents (P1', P2') must
> > preserve that manual layer, but the rewritten parents change the
> > automatic merge, so a simple cherry-pick is wrong: the manual layer
> > would be re-introduced on top of stale auto-merge text.
> > 
> > What works instead is a three-way merge of three trees the existing
> > infrastructure already knows how to compute. Let R be the recursive
> > auto-merge of (P1, P2), O be M's actual tree and N be the recursive
> > auto-merge of (P1', P2'). Then `git diff R O` is morally
> > `git show --remerge-diff M`: it captures exactly what the author
> > added on top of the automatic merge. A non-recursive 3-way merge
> > with R as the merge base, O as side 1 and N as side 2 layers that
> > manual contribution onto the freshly auto-merged rewritten parents
> > (N) and produces the replayed tree.
> 
> So we cherry-pick the difference between the user's conflict resolution O and
> the auto-merge M of the original parents onto the auto-merge N of the replayed
> parents. If we have a topology that looks like
> 
>         |
>        A
>       /|\
>      / B \
>      E  |  D
>         C /
>         |/
>         O
> 
> then running
> 
>     git replay --onto E --ancestry-path B..O
> 
> will replay C and O onto E. If the changes in E and D conflict but those
> conflicts do not overlap with the conflicts in M that were resolved to create
> O then the replayed version of O will contain conflict markers from the
> conflicting changes in E and D. Because the previous conflict resolution
> applies to N without conflicts we do not recognize that there are still
> conflicts in N that need to be resolved.

Very good point, and exactly the kind of feedback I was hoping for when I
marked this as an RFC. Thank you!

> Having realized this I went to look at Elijah's notes and they recognize
> this possibility and suggest extending the xdiff merge code to detect
> when N has conflicts that do not correspond to the conflicts in M. That
> sounds like quite a lot of work. I've not put much effort into coming up
> with a counterexample but think that because "git replay" and "git
> history" do not yet allow the commits in the merged branches to be
> edited we may be able to safely use the implementation proposed in this
> series if both merge parents have been rebased (or we might want all the
> merge bases of the new merge to be a descendants of "--onto"). In the
> example above if both the parents were rebased onto E then any new
> conflicts would happen when picking D rather than when recreating the
> merge.

Right. I have to admit that I missed this corner-case when I looked at the
original notes.

And while `git history`'s `reword` and `split` subcommands won't be
affected, the upcoming `fixup` subcommand _will_ be affected.

I am reworking the patches as we speak, loosely following Elijah's notes.
So far, I'm confident that this will address that problem.

What I am not confident at all so far (because I'm still trying to get the
actual algorithm to work, and haven't had a chance to test this on
real-world scenarios) is that the _conflict output_ is helpful. That is,
whether the conflict markers in case of corner-cases (merge conflicts in
R overlapping with merge conflicts in N, but not being identical, for
example) are clear enough to act upon, or will only lead to despair in the
keen reader.

For example, I noticed that a merge conflict resolution in O that is no
longer necessary in N leads to a quite unhelpful output...

I know that `git replay` is not designed as an interactive tool, but `git
history` is, and will ultimately _have_ to find ways to surface such merge
conflicts and help the user resolve them and then continue the replay.

For now, however, I do agree that we need to capture the error modes
correctly.

Ciao,
Johannes

> 
> Thanks
> 
> Phillip
> 
> > Implement `pick_merge_commit()` along those lines and dispatch to it
> > from `replay_revisions()` when the commit being replayed has exactly
> > two parents. Two specific points (learned the hard way) keep
> > non-trivial cases working where the WIP sketch [2] bailed out.
> > First, R and N use identical `merge_options.branch1` and `branch2`
> > labels ("ours"/"theirs"). When the original parents conflicted on a
> > region of a file, both R and N produce textually identical conflict
> > markers; the outer non-recursive merge then sees N == R in that
> > region and the user's manual resolution from O wins cleanly. Without
> > this, the conflict-marker text would differ between R and N (because
> > the inner merges would label the conflicts differently), and the
> > outer merge would itself be unclean even when the user did supply a
> > clean resolution. Second, an unclean inner merge
> > (`result.clean == 0`) is _not_ fatal: the tree merge-ort produces in
> > that case still has well-defined contents (with conflict markers in
> > the conflicted files) and is a valid input to the outer
> > non-recursive merge. Only a real error (`< 0`) propagates as
> > failure.
> > 
> > The replay propagates the textual diffs the user actually made in M;
> > it does _not_ extrapolate symbol-level intent. If rewriting the
> > parents pulls in genuinely new content (for example, a brand-new
> > caller of a function that the merge renamed), that new content stays
> > as the rewritten parents have it. Symbol-aware refactoring is out of
> > scope here, just as it is for plain rebase.
> > 
> > Octopus merges (more than two parents) and revert-of-merge are not
> > supported and are surfaced as explicit errors at the dispatch point.
> > The "split" sub-command of `git history` continues to refuse when
> > the targeted commit is itself a merge: split semantics do not apply
> > to merges. The pre-walk gate in `builtin/history.c` that previously
> > rejected any merge in the rewrite path now only rejects octopus
> > merges; rename it accordingly.
> > 
> > A small refactor in `create_commit()` makes the merge case possible:
> > the helper now takes a `struct commit_list *parents` rather than a
> > single parent pointer and takes ownership of the list. The single
> > existing caller in `pick_regular_commit()` builds and passes a
> > one-element list; the new `pick_merge_commit()` builds a two-element
> > list, with the order of the `from` and `merge` parents preserved.
> > 
> > Update the negative expectations in t3451, t3452 and t3650 that were
> > asserting the now-retired "not supported yet" message, replacing
> > them with positive coverage where it fits. Octopus rejection and
> > revert-of-merge rejection are covered by new positive tests in
> > t3650. A dedicated test script with merge-replay scenarios driven by
> > a new test-tool fixture builder will follow in a subsequent commit.
> > 
> > [1] https://github.com/newren/git/blob/replay/replay-design-notes.txt
> > [2]
> > https://github.com/newren/git/commit/4c45e8955ef9bf7d01fd15d9106b3bdb8ea91b45
> > 
> > Helped-by: Elijah Newren <newren@gmail.com>
> > Assisted-by: Claude Opus 4.7
> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> > ---
> >   builtin/history.c         |  16 ++-
> >   replay.c                  | 209 ++++++++++++++++++++++++++++++++++++--
> >   t/t3451-history-reword.sh |  21 ++--
> >   t/t3452-history-split.sh  |   6 +-
> >   t/t3650-replay-basics.sh  |  46 ++++++++-
> >   5 files changed, 269 insertions(+), 29 deletions(-)
> > 
> > diff --git a/builtin/history.c b/builtin/history.c
> > index 9526938085..00097b2226 100644
> > --- a/builtin/history.c
> > +++ b/builtin/history.c
> > @@ -195,15 +195,15 @@ static int parse_ref_action(const struct option *opt,
> > const char *value, int uns
> >   	return 0;
> >   }
> >   
> > -static int revwalk_contains_merges(struct repository *repo,
> > -				   const struct strvec *revwalk_args)
> > +static int revwalk_contains_octopus_merges(struct repository *repo,
> > +					   const struct strvec *revwalk_args)
> >   {
> >    struct strvec args = STRVEC_INIT;
> >    struct rev_info revs;
> >    int ret;
> >   
> >   	strvec_pushv(&args, revwalk_args->v);
> > -	strvec_push(&args, "--min-parents=2");
> > +	strvec_push(&args, "--min-parents=3");
> >   
> >    repo_init_revisions(repo, &revs, NULL);
> >   @@ -217,7 +217,7 @@ static int revwalk_contains_merges(struct repository
> > *repo,
> >    }
> >   
> >   	if (get_revision(&revs)) {
> > -		ret = error(_("replaying merge commits is not supported
> > yet!"));
> > +		ret = error(_("replaying octopus merges is not supported"));
> >    	goto out;
> >    }
> >   @@ -289,7 +289,7 @@ static int setup_revwalk(struct repository *repo,
> >    	strvec_push(&args, "HEAD");
> >    }
> >   -	ret = revwalk_contains_merges(repo, &args);
> > +	ret = revwalk_contains_octopus_merges(repo, &args);
> >    if (ret < 0)
> >     goto out;
> >   @@ -482,6 +482,9 @@ static int cmd_history_reword(int argc,
> >    if (ret < 0) {
> >     ret = error(_("failed replaying descendants"));
> >     goto out;
> > +	} else if (ret) {
> > +		ret = error(_("conflict during replay; some descendants were
> > not rewritten"));
> > +		goto out;
> >    }
> >   
> >   	ret = 0;
> > @@ -721,6 +724,9 @@ static int cmd_history_split(int argc,
> >    if (ret < 0) {
> >     ret = error(_("failed replaying descendants"));
> >     goto out;
> > +	} else if (ret) {
> > +		ret = error(_("conflict during replay; some descendants were
> > not rewritten"));
> > +		goto out;
> >    }
> >   
> >   	ret = 0;
> > diff --git a/replay.c b/replay.c
> > index f96f1f6551..3dbce095f9 100644
> > --- a/replay.c
> > +++ b/replay.c
> > @@ -1,6 +1,7 @@
> >   #define USE_THE_REPOSITORY_VARIABLE
> >   
> >   #include "git-compat-util.h"
> > +#include "commit-reach.h"
> >   #include "environment.h"
> >   #include "hex.h"
> >   #include "merge-ort.h"
> > @@ -77,15 +78,21 @@ static void generate_revert_message(struct strbuf *msg,
> >   	repo_unuse_commit_buffer(repo, commit, message);
> >   }
> >   
> > +/*
> > + * Build a new commit with the given tree and parent list, copying author,
> > + * extra headers and (for pick mode) the commit message from `based_on`.
> > + *
> > + * Takes ownership of `parents`: it will be freed before returning, even on
> > + * error. Parent order is preserved as supplied by the caller.
> > + */
> >   static struct commit *create_commit(struct repository *repo,
> >           struct tree *tree,
> >           struct commit *based_on,
> > -				    struct commit *parent,
> > +				    struct commit_list *parents,
> >   				    enum replay_mode mode)
> >   {
> >    struct object_id ret;
> >    struct object *obj = NULL;
> > -	struct commit_list *parents = NULL;
> >    char *author = NULL;
> >    char *sign_commit = NULL; /* FIXME: cli users might want to sign again */
> >    struct commit_extra_header *extra = NULL;
> > @@ -96,7 +103,6 @@ static struct commit *create_commit(struct repository
> > *repo,
> >    const char *orig_message = NULL;
> >    const char *exclude_gpgsig[] = { "gpgsig", "gpgsig-sha256", NULL };
> >   -	commit_list_insert(parent, &parents);
> >    extra = read_commit_extra_headers(based_on, exclude_gpgsig);
> >    if (mode == REPLAY_MODE_REVERT) {
> >   		generate_revert_message(&msg, based_on, repo);
> > @@ -273,6 +279,7 @@ static struct commit *pick_regular_commit(struct
> > repository *repo,
> >   {
> >    struct commit *base, *replayed_base;
> >    struct tree *pickme_tree, *base_tree, *replayed_base_tree;
> > +	struct commit_list *parents = NULL;
> >   
> >    if (pickme->parents) {
> >   		base = pickme->parents->item;
> > @@ -327,7 +334,143 @@ static struct commit *pick_regular_commit(struct
> > repository *repo,
> >    if (oideq(&replayed_base_tree->object.oid, &result->tree->object.oid) &&
> >        !oideq(&pickme_tree->object.oid, &base_tree->object.oid))
> >   		return replayed_base;
> > -	return create_commit(repo, result->tree, pickme, replayed_base, mode);
> > +	commit_list_insert(replayed_base, &parents);
> > +	return create_commit(repo, result->tree, pickme, parents, mode);
> > +}
> > +
> > +/*
> > + * Replay a 2-parent merge commit by composing three calls into merge-ort:
> > + *
> > + *   R = recursive merge of pickme's two original parents (auto-remerge of
> > + *       the original merge, accepting any conflicts)
> > + *   N = recursive merge of the (possibly rewritten) parents
> > + *   O = pickme's tree (the user's actual merge, including any manual
> > + *       resolutions)
> > + *
> > + * The picked tree comes from a non-recursive merge using R as the base,
> > + * O as side1 and N as side2. `git diff R O` is morally `git show
> > + * --remerge-diff $oldmerge`, so this layers the user's original manual
> > + * resolution on top of the freshly auto-merged rewritten parents (see
> > + * `replay-design-notes.txt` on the `replay` branch of newren/git).
> > + *
> > + * If the outer 3-way merge is unclean, propagate the conflict status to
> > + * the caller via `result->clean = 0` and return NULL. The two inner
> > + * merges (R and N) being unclean is _not_ fatal: the conflict-markered
> > + * trees they produce are valid inputs to the outer merge, and using
> > + * identical labels for both inner merges keeps the marker text
> > + * byte-equal between R and N so the user's resolution recorded in O
> > + * collapses the conflict cleanly there. Octopus merges (more than two
> > + * parents) and revert-of-merge are rejected by the caller before this
> > + * function is invoked.
> > + */
> > +static struct commit *pick_merge_commit(struct repository *repo,
> > +					struct commit *pickme,
> > +					kh_oid_map_t *replayed_commits,
> > +					struct merge_options *merge_opt,
> > +					struct merge_result *result)
> > +{
> > +	struct commit *parent1, *parent2;
> > +	struct commit *replayed_par1, *replayed_par2;
> > +	struct tree *pickme_tree;
> > +	struct merge_options remerge_opt = { 0 };
> > +	struct merge_options new_merge_opt = { 0 };
> > +	struct merge_result remerge_res = { 0 };
> > +	struct merge_result new_merge_res = { 0 };
> > +	struct commit_list *parent_bases = NULL;
> > +	struct commit_list *replayed_bases = NULL;
> > +	struct commit_list *parents;
> > +	struct commit *picked = NULL;
> > +	char *ancestor_name = NULL;
> > +
> > +	parent1 = pickme->parents->item;
> > +	parent2 = pickme->parents->next->item;
> > +
> > +	/*
> > +	 * Map the merge's parents to their replayed counterparts. With the
> > +	 * boundary commits pre-seeded into `replayed_commits`, every parent
> > +	 * either has an explicit mapping (rewritten or boundary -> onto) or
> > +	 * sits outside the rewrite range entirely; the latter must stay at
> > +	 * the original parent commit, so use `parent` itself as the fallback
> > +	 * for both sides.
> > +	 */
> > +	replayed_par1 = mapped_commit(replayed_commits, parent1, parent1);
> > +	replayed_par2 = mapped_commit(replayed_commits, parent2, parent2);
> > +
> > +	/*
> > +	 * R: auto-remerge of the original parents.
> > +	 *
> > +	 * Use the same branch labels for the inner merges that compute R
> > +	 * and N so conflict markers (if any) are textually identical
> > +	 * between the two; the outer non-recursive merge can then collapse
> > +	 * the manual resolution from O against them.
> > +	 */
> > +	init_basic_merge_options(&remerge_opt, repo);
> > +	remerge_opt.show_rename_progress = 0;
> > +	remerge_opt.branch1 = "ours";
> > +	remerge_opt.branch2 = "theirs";
> > +	if (repo_get_merge_bases(repo, parent1, parent2, &parent_bases) < 0) {
> > +		result->clean = -1;
> > +		goto out;
> > +	}
> > +	merge_incore_recursive(&remerge_opt, parent_bases,
> > +			       parent1, parent2, &remerge_res);
> > +	parent_bases = NULL; /* consumed by merge_incore_recursive */
> > +	if (remerge_res.clean < 0) {
> > +		result->clean = remerge_res.clean;
> > +		goto out;
> > +	}
> > +
> > +	/* N: fresh merge of the (possibly rewritten) parents. */
> > +	init_basic_merge_options(&new_merge_opt, repo);
> > +	new_merge_opt.show_rename_progress = 0;
> > +	new_merge_opt.branch1 = "ours";
> > +	new_merge_opt.branch2 = "theirs";
> > +	if (repo_get_merge_bases(repo, replayed_par1, replayed_par2,
> > +				 &replayed_bases) < 0) {
> > +		result->clean = -1;
> > +		goto out;
> > +	}
> > +	merge_incore_recursive(&new_merge_opt, replayed_bases,
> > +			       replayed_par1, replayed_par2, &new_merge_res);
> > +	replayed_bases = NULL; /* consumed by merge_incore_recursive */
> > +	if (new_merge_res.clean < 0) {
> > +		result->clean = new_merge_res.clean;
> > +		goto out;
> > +	}
> > +
> > +	/*
> > +	 * Outer non-recursive merge: base=R, side1=O (pickme), side2=N.
> > +	 */
> > +	pickme_tree = repo_get_commit_tree(repo, pickme);
> > +	ancestor_name = xstrfmt("auto-remerge of %s",
> > +				oid_to_hex(&pickme->object.oid));
> > +	merge_opt->ancestor = ancestor_name;
> > +	merge_opt->branch1 = short_commit_name(repo, pickme);
> > +	merge_opt->branch2 = "merge of replayed parents";
> > +	merge_incore_nonrecursive(merge_opt,
> > +				  remerge_res.tree,
> > +				  pickme_tree,
> > +				  new_merge_res.tree,
> > +				  result);
> > +	merge_opt->ancestor = NULL;
> > +	merge_opt->branch1 = NULL;
> > +	merge_opt->branch2 = NULL;
> > +	if (!result->clean)
> > +		goto out;
> > +
> > +	parents = NULL;
> > +	commit_list_insert(replayed_par2, &parents);
> > +	commit_list_insert(replayed_par1, &parents);
> > +	picked = create_commit(repo, result->tree, pickme, parents,
> > +			       REPLAY_MODE_PICK);
> > +
> > +out:
> > +	free(ancestor_name);
> > +	free_commit_list(parent_bases);
> > +	free_commit_list(replayed_bases);
> > +	merge_finalize(&remerge_opt, &remerge_res);
> > +	merge_finalize(&new_merge_opt, &new_merge_res);
> > +	return picked;
> >   }
> >   
> >   void replay_result_release(struct replay_result *result)
> > @@ -407,17 +550,63 @@ int replay_revisions(struct rev_info *revs,
> >    merge_opt.show_rename_progress = 0;
> >    last_commit = onto;
> >    replayed_commits = kh_init_oid_map();
> > +
> > +	/*
> > +	 * Seed the rewritten-commit map with each negative-side ("BOTTOM")
> > +	 * cmdline entry pointing at `onto`. This matters for merge replay:
> > +	 * a 2-parent merge whose first parent is the boundary (e.g. the
> > +	 * commit being reworded) must replay onto the rewritten boundary,
> > +	 * yet pick_merge_commit uses a self fallback so the second parent
> > +	 * (a side branch outside the rewrite range) is preserved as-is.
> > +	 * Pre-seeding the boundary disambiguates the two: in the map ->
> > +	 * rewritten, missing -> kept as-is.
> > +	 *
> > +	 * Only do this for the pick path; revert mode chains reverts
> > +	 * through last_commit and a pre-seeded boundary would short-circuit
> > +	 * that chain.
> > +	 */
> > +	if (mode == REPLAY_MODE_PICK) {
> > +		for (size_t i = 0; i < revs->cmdline.nr; i++) {
> > +			struct rev_cmdline_entry *e = &revs->cmdline.rev[i];
> > +			struct commit *boundary;
> > +			khint_t pos;
> > +			int hr;
> > +
> > +			if (!(e->flags & BOTTOM))
> > +				continue;
> > +			boundary = lookup_commit_reference_gently(revs->repo,
> > +
> > &e->item->oid, 1);
> > +			if (!boundary)
> > +				continue;
> > +			pos = kh_put_oid_map(replayed_commits,
> > +					     boundary->object.oid, &hr);
> > +			if (hr != 0)
> > +				kh_value(replayed_commits, pos) = onto;
> > +		}
> > +	}
> > +
> >    while ((commit = get_revision(revs))) {
> >     const struct name_decoration *decoration;
> >     khint_t pos;
> >     int hr;
> >   -		if (commit->parents && commit->parents->next)
> > -			die(_("replaying merge commits is not supported
> > yet!"));
> > -
> > -		last_commit = pick_regular_commit(revs->repo, commit,
> > replayed_commits,
> > -						  mode == REPLAY_MODE_REVERT ?
> > last_commit : onto,
> > -						  &merge_opt, &result, mode);
> > +		if (commit->parents && commit->parents->next) {
> > +			if (commit->parents->next->next) {
> > +				ret = error(_("replaying octopus merges is not
> > supported"));
> > +				goto out;
> > +			}
> > +			if (mode == REPLAY_MODE_REVERT) {
> > +				ret = error(_("reverting merge commits is not
> > supported"));
> > +				goto out;
> > +			}
> > +			last_commit = pick_merge_commit(revs->repo, commit,
> > +							replayed_commits,
> > +							&merge_opt, &result);
> > +		} else {
> > +			last_commit = pick_regular_commit(revs->repo, commit,
> > replayed_commits,
> > +							  mode ==
> > REPLAY_MODE_REVERT ? last_commit : onto,
> > +							  &merge_opt, &result,
> > mode);
> > +		}
> >     if (!last_commit)
> >      break;
> >   diff --git a/t/t3451-history-reword.sh b/t/t3451-history-reword.sh
> > index de7b357685..d103f866a2 100755
> > --- a/t/t3451-history-reword.sh
> > +++ b/t/t3451-history-reword.sh
> > @@ -201,12 +201,21 @@ test_expect_success 'can reword a merge commit' '
> >     git switch - &&
> >     git merge theirs &&
> >   -		# It is not possible to replay merge commits embedded in the
> > -		# history (yet).
> > -		test_must_fail git -c core.editor=false history reword HEAD~
> > 2>err &&
> > -		test_grep "replaying merge commits is not supported yet" err
> > &&
> > +		# Reword a non-merge commit whose descendants include the
> > +		# merge: replay carries the merge through.
> > +		reword_with_message HEAD~ <<-EOF &&
> > +		ours reworded
> > +		EOF
> > +		expect_graph <<-EOF &&
> > +		*   Merge tag ${SQ}theirs${SQ}
> > +		|\\
> > +		| * theirs
> > +		* | ours reworded
> > +		|/
> > +		* base
> > +		EOF
> >   -		# But it is possible to reword a merge commit directly.
> > +		# And reword a merge commit directly.
> >     reword_with_message HEAD <<-EOF &&
> >     Reworded merge commit
> >     EOF
> > @@ -214,7 +223,7 @@ test_expect_success 'can reword a merge commit' '
> >     *   Reworded merge commit
> > |\
> > | * theirs
> > -		* | ours
> > +		* | ours reworded
> > |/
> >     * base
> >     EOF
> > diff --git a/t/t3452-history-split.sh b/t/t3452-history-split.sh
> > index 8ed0cebb50..ad6309f98b 100755
> > --- a/t/t3452-history-split.sh
> > +++ b/t/t3452-history-split.sh
> > @@ -36,7 +36,7 @@ expect_tree_entries () {
> >   	test_cmp expect actual
> >   }
> >   
> > -test_expect_success 'refuses to work with merge commits' '
> > +test_expect_success 'refuses to split a merge commit' '
> >    test_when_finished "rm -rf repo" &&
> >    git init repo &&
> >    (
> > @@ -49,9 +49,7 @@ test_expect_success 'refuses to work with merge commits' '
> >     git switch - &&
> >     git merge theirs &&
> >     test_must_fail git history split HEAD 2>err &&
> > -		test_grep "cannot split up merge commit" err &&
> > -		test_must_fail git history split HEAD~ 2>err &&
> > -		test_grep "replaying merge commits is not supported yet" err
> > +		test_grep "cannot split up merge commit" err
> > )
> >   '
> >   
> > diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
> > index 3353bc4a4d..368b1b0f9a 100755
> > --- a/t/t3650-replay-basics.sh
> > +++ b/t/t3650-replay-basics.sh
> > @@ -103,10 +103,48 @@ test_expect_success 'cannot advance target ...
> > ordering would be ill-defined' '
> >   	test_cmp expect actual
> >   '
> >   
> > -test_expect_success 'replaying merge commits is not supported yet' '
> > -	echo "fatal: replaying merge commits is not supported yet!" >expect &&
> > -	test_must_fail git replay --advance=main main..topic-with-merge
> > 2>actual &&
> > -	test_cmp expect actual
> > +test_expect_success 'using replay to rebase a 2-parent merge' '
> > +	# main..topic-with-merge contains a 2-parent merge (P) introduced
> > +	# via test_merge. Use --ref-action=print so this test does not
> > +	# mutate state for subsequent tests in this file.
> > +	git replay --ref-action=print --onto main main..topic-with-merge
> > >result &&
> > +	test_line_count = 1 result &&
> > +
> > +	new_tip=$(cut -f 3 -d " " result) &&
> > +
> > +	# Result is still a 2-parent merge.
> > +	git cat-file -p $new_tip >cat &&
> > +	grep -c "^parent " cat >count &&
> > +	echo 2 >expect &&
> > +	test_cmp expect count &&
> > +
> > +	# Merge subject is preserved.
> > +	echo P >expect &&
> > +	git log -1 --format=%s $new_tip >actual &&
> > +	test_cmp expect actual &&
> > +
> > +	# The replayed merge sits on top of main: walking back via the
> > +	# first-parent chain reaches main.
> > +	git merge-base --is-ancestor main $new_tip
> > +'
> > +
> > +test_expect_success 'replaying an octopus merge is rejected' '
> > +	# Build an octopus side-branch so the rest of the test state stays
> > +	# untouched.
> > +	test_when_finished "git update-ref -d refs/heads/octopus-tip" &&
> > +	octopus_tip=$(git commit-tree -p topic4 -p topic1 -p topic3 \
> > +		-m "octopus" $(git rev-parse topic4^{tree})) &&
> > +	git update-ref refs/heads/octopus-tip "$octopus_tip" &&
> > +
> > +	test_must_fail git replay --ref-action=print --onto main \
> > +		topic4..octopus-tip 2>actual &&
> > +	test_grep "octopus merges" actual
> > +'
> > +
> > +test_expect_success 'reverting a merge commit is rejected' '
> > +	test_must_fail git replay --ref-action=print --revert=topic-with-merge
> > \
> > +		topic4..topic-with-merge 2>actual &&
> > +	test_grep "reverting merge commits" actual
> >   '
> >   
> >   test_expect_success 'using replay to rebase two branches, one on top of
> >   other' '
> 
> 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox