Git development
 help / color / mirror / Atom feed
* Re: [PATCH] completion: complete paths for git send-email
From: Junio C Hamano @ 2026-07-21 17:09 UTC (permalink / raw)
  To: D. Ben Knoble
  Cc: Yury Norov (NVIDIA), git, Thiago Perrotta, Philippe Blain,
	Rubén Justo, Yury Norov, linux-kernel, Codex
In-Reply-To: <CALnO6CAuitGp_xLYkXpkQYV9oiXsNNfsXZ_OqzkW7_6ND49=LA@mail.gmail.com>

"D. Ben Knoble" <ben.knoble@gmail.com> writes:

> On Sun, Jul 19, 2026 at 9:45 AM Yury Norov (NVIDIA)
> <yury.norov@gmail.com> wrote:
>>
>> From: Yury Norov <ynorov@nvidia.com>
>>
>> git send-email accepts either revisions or paths to patch files, but its
>> Bash completion only offers revisions. This prevents patch files from
>> being completed. It can also make a prefix such as "0" expand to an
>> unrelated hexadecimal ref even when matching 0001-*.patch files exist.
>>
>> In my Linux tree, an attempt to autocomplete the standard-named patch
>> brings a random hashtag:
>
> It is unusual to call this a "hashtag." Perhaps "hash" or "object
> name" (or id) based on the glossary and datamodel docs?

Very good point, but I am not sure if the author truly meant object
names here.  The reproduction test uses a long hexadecimal string,
but that is not an object name; it is an unusual-looking tag name.
It is like naming a topic branch '012345' and complaining that:

    $ git send-email 0<TAB>

completes the input to the branch name while ignoring the
0001-changes.patch file.

When you have a branch named '0-tolerance-policy' and:

    $ git send-email 0<TAB>

completes to that branch name, you would not dream of complaining
about the completion.  IOW, I think the complaint is somewhat unfair
to begin with.

Actually, I do not know if the completion script really expands an
abbreviated object name to a full one.  I tried:

    $ git rev-parse seen^2
    179eccf0d01729c19a3238905b951b1880aa4ba1
    $ git checkout master
    $ . contrib/completion/git-completion.bash
    $ git send-email 17<TAB>

and waited for some time, but it did not complete to anything.

In any case, when both a '0001-my-changes.patch' file and a
'0-tolerance-policy' branch exist in your repository and current
working directory, running:

    $ git send-email 0<TAB>

should offer both as candidates, I thihk.  Since I only ever pass
filenames to the command, I personally do not think it is a huge
loss if the completion script stops looking at refs and sticks to
filenames only, but others may have a use for that feature.


^ permalink raw reply

* Re: [PATCH 2/2] remote: resolve URL-valued push tracking remotes
From: Junio C Hamano @ 2026-07-21 16:11 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
In-Reply-To: <ff645b21591a4b365b30acaf67a295510889141c.1784538618.git.gitgitgadget@gmail.com>

"Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com> writes:

> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> A branch may name its push destination with a URL instead of a
> configured remote. This is useful in fork workflows, where the original
> remote is renamed to "upstream", the fork is added as "origin", and an
> existing branch.<name>.pushRemote continues to contain the fork URL.
>
> Git can still push through the anonymous remote created for that URL.
> However, the anonymous remote has no fetch refspec. Git therefore cannot
> resolve @{push} to origin/<branch> or update that remote-tracking branch
> after a push. The push can succeed, or report that everything is up to
> date, while status continues to compare against a stale tracking ref or
> cannot show the push branch at all.
>
> A uniquely matching configured remote already provides the missing
> mapping. Use its fetch refspec when resolving the push tracking branch
> and when updating tracking refs after a push. This changes neither the
> push destination nor configuration. Keep the existing behavior when no
> remote matches or multiple remotes share the URL, since either case is
> ambiguous.
> ...
> +struct remote *repo_remote_for_push_tracking(struct repository *repo,
> +					     struct remote *remote)
> +{
> +	struct remote *first_match = NULL;
> +	struct remote_state *remote_state = repo->remote_state;
> +
> +	if (remote->origin != REMOTE_UNCONFIGURED || remote->url.nr != 1)
> +		return remote;

I briefly wondered what should happen when a caller passes NULL as
the remote parameter to this function, but it turns out that no
caller passes NULL.  One caller is tracking_for_push_dest(),
which is called from branch_get_push_1().  The latter refuses to
proceed when !remote is true and does not call
tracking_for_push_dest(), meaning it cannot pass NULL to this
function.  The other caller is transport_push(), which passes
transport->remote.  This value comes from transport_get(), which
ensures transport->remote is not NULL before returning, so it
cannot pass NULL to this function either.

Therefore, it is OK to assume remote is not NULL, and let the
program crash loudly if that assumption is violated.  Adding an
explicit BUG() check would be overkill here:

    if (!repo || !remote)
            BUG("...");

> +	for (int i = 0; i < remote_state->remotes_nr; i++) {
> +		struct remote *candidate = remote_state->remotes[i];
> +
> +		if (!candidate || candidate == remote ||
> +		    !remote_is_configured(candidate, 0) ||
> +		    !remote_has_url(candidate, remote->url.v[0]))
> +			continue;

This check, as well as the safety uniqueness check at the beginning
of the function, only pays attention to the url member.  However, it
should also consider the pushurl member and, when it exists, ignore
the url member.  The upfront check would then look something like
this (please sanity check the details):

	const char *check_url = NULL;

	if (remote->origin != REMOTE_UNCONFIGURED)
		return remote;

	if (remote->pushurl.nr) {
		if (remote->pushurl.nr != 1)
			return remote;
		check_url = remote->pushurl.v[0];
	} else if (remote->url.nr != 1) {
		return remote;
	} else {
		check_url = remote->url.v[0];
	}

The test inside the loop would then use check_url:

		!remote_has_url(candidate, check_url)

instead of testing remote->url.v[0] directly.

Thanks.


^ permalink raw reply

* Re: Performance regression in connectivity check during receive-pack (git 2.54)
From: Junio C Hamano @ 2026-07-21 14:40 UTC (permalink / raw)
  To: Jeff King; +Cc: Wolfgang Kritzinger, Patrick Steinhardt, git
In-Reply-To: <20260721035733.GA581473@coredump.intra.peff.net>

Jeff King <peff@peff.net> writes:

> Yeah, and that type of regression makes sense for what a593373b09 was
> trying to do. But I think the v2.54 behavior is wrong. We should check
> all packs before any loose objects.
>
> I'm not sure of the correct fix. This is working against the whole "odb
> sources are independent and abstract" refactoring that a593373b09 was
> going for. But I think it's an important optimization. I guess the
> abstract version would be that each source has "fast" and "slow" lookups
> or something like that, and we check all fast ones before slow ones. But
> that is pretty gross.
>
> I'll leave it to Patrick to ponder further. I haven't really been paying
> a lot of attention to the odb refactoring.

I think checking the fast sources before the slow ones is probably
the best we can do if we want to retain the 'each odb source is an
opaque object' abstraction.

Stepping back a bit, the 'rev-list' command used for the
connectivity check is curious in multiple aspects.

 * On the surface, it looks as if the caller wants an enumeration of
   all objects that appear in the range.  However, the caller is not
   interested in the actual list of objects.  Instead, they are
   interested only in a single bit: whether the traversal succeeds
   or dies due to a missing object.  This is because the traversal
   determines whether we need to fetch, or whether we are already up
   to date, to decide whether the proposed 'fetch' is a no-op.  The
   positive ends of the traversal represent what we are about to
   fetch; if we already have all the objects needed to reach those
   tips in our repository, we can do without actually downloading
   anything [*].

 * A false positive answer to the question "does the traversal die
   due to a missing object?" does not affect correctness, as this is
   merely an optimization to save downloads (though a false negative
   is unacceptable).

Given this non-standard use of the command, we can pass
application-specific cues (such as "we are doing this traversal for
a connectivity check") down to the machinery as a hint to help it
optimize its operation, and I suspect that such a hint might have
value.

For example, we could enumerate all loose objects in the loose
object store using 256 opendir() and readdir() calls for about
10,000 files (since once you have more than 6,700 loose objects,
auto-gc would pack them) and store them in an in-core table [**].
This would enable us to say "the object with that name does not
exist here" without running lstat() at all.  I wonder how many
lstat() calls we would need to save for such a scheme to pay off.

There may be other highly application-specific optimization
opportunities, as utilizing revision traversal for
connectivity checking has peculiar correctness requirements
that differ from the normal use of the API.

[Footnote]

 * It follows that in a lazily cloned repository with promisor
   remotes, the traversal could download everything needed as it
   goes, only to conclude: "No need for the main fetch; we have
   everything we need."  I would expect this to be a fairly slow
   process that defeats the entire reason we have this connectivity
   check up front as an optimization.  While I have not checked, I
   believe the actual code prevents this either by skipping the
   connectivity check altogether, or by instructing the connectivity
   checker to treat promised (but not immediately available) objects
   as missing and abort.  But my point is that theoretically one
   does not even need 'git fetch' in a lazily cloned repository.  It
   is sufficient to use 'git ls-remote' to determine the tips of
   remote refs, and run 'rev-list' to fill the range.

** If in-core memory pressure is a concern, we could use a Bloom
   filter, as we only need to know "the object is definitely not
   here" and can tolerate "that object might be here, but we are not
   certain."

^ permalink raw reply

* Re: [PATCH v2 0/2] remote: renamed remote push tracking
From: D. Ben Knoble @ 2026-07-21 14:28 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
In-Reply-To: <pull.2358.v2.git.git.1784624306.gitgitgadget@gmail.com>

Hi Harald,

On Tue, Jul 21, 2026 at 5:08 AM Harald Nordgren via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> Keep git status showing the push branch after remotes are renamed by finding
> the configured remote with the same URL.
>
> Changes in v3:
>
>  * Revamp commit messages to clarify motivation.
>
> Changes in v2:
>
>  * Clarify that URL push destinations already work and that this change only
>    restores their tracking information.
>  * Document URL values for branch.<name>.pushRemote and their @{push}
>    behavior.
>
> Harald Nordgren (2):
>   remote: pass repository to push tracking helper
>   remote: find tracking branches for URL push destinations
>
>  Documentation/config/branch.adoc |   2 +
>  Documentation/revisions.adoc     |   3 +
>  remote.c                         |  36 +++++++++--
>  remote.h                         |   2 +
>  t/t5505-remote.sh                | 104 +++++++++++++++++++++++++++++++
>  transport.c                      |   5 +-
>  6 files changed, 146 insertions(+), 6 deletions(-)
>
>
> base-commit: 48bbf81c29ca9a4479ec7850fe206518682cdb2f
> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2358%2FHaraldNordgren%2Fremote-resolve-url-push-tracking-v2
> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2358/HaraldNordgren/remote-resolve-url-push-tracking-v2
> Pull-Request: https://github.com/git/git/pull/2358
>
> Range-diff vs v1:
>
>  1:  fc70895732 ! 1:  b1ac49de87 remote: pass repository to push tracking helper
>      @@ Metadata
>        ## Commit message ##
>           remote: pass repository to push tracking helper
>
>      -    The push tracking helper currently only needs the push remote. However,
>      -    resolving a URL-valued remote requires access to the repository's list
>      -    of configured remotes.
>      +    The next commit needs tracking_for_push_dest() to inspect the
>      +    repository's configured remotes. Pass the repository through the
>      +    existing callers and mark the new parameter as unused.
>
>      -    Pass the repository through the existing callers and mark the parameter
>      -    as unused for now. This prepares the helper for that lookup without
>      -    changing its behavior.
>      +    No change in behavior.
>
>           Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
>
>  2:  ff645b2159 ! 2:  6e924a7fec remote: resolve URL-valued push tracking remotes
>      @@ Metadata
>       Author: Harald Nordgren <haraldnordgren@gmail.com>
>
>        ## Commit message ##
>      -    remote: resolve URL-valued push tracking remotes
>      +    remote: find tracking branches for URL push destinations
>
>      -    A branch may name its push destination with a URL instead of a
>      -    configured remote. This is useful in fork workflows, where the original
>      -    remote is renamed to "upstream", the fork is added as "origin", and an
>      -    existing branch.<name>.pushRemote continues to contain the fork URL.
>      +    Git already accepts a repository URL as branch.<name>.pushRemote and
>      +    can push to it. When a configured remote has the same URL, however,
>      +    "git status" cannot show that remote's push branch.
>
>      -    Git can still push through the anonymous remote created for that URL.
>      -    However, the anonymous remote has no fetch refspec. Git therefore cannot
>      -    resolve @{push} to origin/<branch> or update that remote-tracking branch
>      -    after a push. The push can succeed, or report that everything is up to
>      -    date, while status continues to compare against a stale tracking ref or
>      -    cannot show the push branch at all.
>      +    This can happen in fork workflows when the original remote is renamed
>      +    to "upstream", the fork is added as "origin", and an existing
>      +    pushRemote value still contains the fork URL. The URL still points to
>      +    the right repository, so pushing works. However, @{push} is unavailable
>      +    because Git does not connect the URL to "origin". As a result,
>      +    "git status" cannot show the push branch, and an up-to-date push can
>      +    leave its local tracking information stale.

I'm a bit confused about the problem scenario here: if the pushRemote
value contains a URL, then renaming a remote has nothing to do with
it, right?

And if the pushRemote value contains a remote name, then renaming the
remote should propagate there as well, right? (At least, that's my
recollection of renaming; when I have used the GitHub CLI in the past
it has worked pretty well in that case, but maybe they've changed
things recently?)

I do think the URL<->remote matching for user display is a nice touch,
so I'm not against the series! Just want to understand the problem
statement well. Maybe I should read over the test cases, or you could
suggest a "how I hit this in the real world" recipe? (Explicit
commands are easier for me than natural language in that case.)

-- 
D. Ben Knoble

^ permalink raw reply

* [PATCH 1/1] rebase: add --[no-]edit to --continue
From: Hugo Sales @ 2026-07-21 14:04 UTC (permalink / raw)
  To: git
  Cc: Hugo Sales, Phillip Wood, Junio C Hamano,
	Ævar Arnfjörð Bjarmason, Patrick Steinhardt,
	Elijah Newren
In-Reply-To: <20260721140443.1809379-1-hugo@hsal.es>

Allow skipping the editor when continuing after resolving conflicts,
via --no-edit or the rebase.noEdit configuration variable. The --edit
option overrides rebase.noEdit when both are set.

Signed-off-by: Hugo Sales <hugo@hsal.es>
---
 Documentation/config/rebase.adoc |  6 ++++
 Documentation/git-rebase.adoc    | 17 +++++++++--
 builtin/rebase.c                 | 29 ++++++++++++++++--
 sequencer.c                      | 29 +++++++++++++++++-
 t/t3436-rebase-more-options.sh   | 52 ++++++++++++++++++++++++++++++++
 5 files changed, 126 insertions(+), 7 deletions(-)

diff --git a/Documentation/config/rebase.adoc b/Documentation/config/rebase.adoc
index c6187ab28b..321ab8b529 100644
--- a/Documentation/config/rebase.adoc
+++ b/Documentation/config/rebase.adoc
@@ -62,6 +62,12 @@ instead of:
 +
 Defaults to false.
 
+rebase.noEdit::
+	When set to true, `git rebase --continue` uses the commit message
+	without launching $EDITOR, as if `--no-edit` were given.  The
+	`--edit` option to `git rebase --continue` overrides this setting.
+	Defaults to false.
+
 rebase.rescheduleFailedExec::
 	Automatically reschedule `exec` commands that failed. This only makes
 	sense in interactive mode (or when an `--exec` option was provided).
diff --git a/Documentation/git-rebase.adoc b/Documentation/git-rebase.adoc
index f6c22d1598..cc0a69b5a5 100644
--- a/Documentation/git-rebase.adoc
+++ b/Documentation/git-rebase.adoc
@@ -181,6 +181,16 @@ including not with each other:
 
 --continue::
 	Restart the rebasing process after having resolved a merge conflict.
++
+-e::
+--edit::
+--no-edit::
+	With `--continue`, edit or do not edit the commit message,
+	respectively. By default, the configured $EDITOR is opened so you
+	can update the commit message after resolving conflicts.
+	`--no-edit` reuses the existing message without launching an
+	editor. The `rebase.noEdit` configuration variable can be used to
+	enable `--no-edit` by default; `--edit` overrides that setting.
 
 --skip::
 	Restart the rebasing process by skipping the current patch.
@@ -783,9 +793,10 @@ Commit Rewording
 When a conflict occurs while rebasing, rebase stops and asks the user
 to resolve.  Since the user may need to make notable changes while
 resolving conflicts, after conflicts are resolved and the user has run
-`git rebase --continue`, the rebase should open an editor and ask the
-user to update the commit message.  The 'merge' backend does this, while
-the 'apply' backend blindly applies the original commit message.
+`git rebase --continue`, the rebase opens an editor and asks the
+user to update the commit message, unless `rebase.noEdit` is set or
+`--no-edit` is passed to `--continue`.  The 'merge' backend does this,
+while the 'apply' backend blindly applies the original commit message.
 
 Miscellaneous differences
 ~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/rebase.c b/builtin/rebase.c
index 10a306310c..5827b20baf 100644
--- a/builtin/rebase.c
+++ b/builtin/rebase.c
@@ -43,7 +43,7 @@ static char const * const builtin_rebase_usage[] = {
 		"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]"),
 	N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
 		"--root [<branch>]"),
-	"git rebase --continue | --abort | --skip | --edit-todo",
+	"git rebase --continue [--[no-]edit] | --abort | --skip | --edit-todo",
 	NULL
 };
 
@@ -135,6 +135,8 @@ struct rebase_options {
 	int config_autosquash;
 	int config_rebase_merges;
 	int config_update_refs;
+	int config_no_edit;
+	int edit;
 };
 
 #define REBASE_OPTIONS_INIT {			  	\
@@ -156,6 +158,8 @@ struct rebase_options {
 		.update_refs = -1,                      \
 		.config_update_refs = -1,               \
 		.strategy_opts = STRING_LIST_INIT_NODUP,\
+		.config_no_edit = -1,                   \
+		.edit = -1,                             \
 	}
 
 static void rebase_options_release(struct rebase_options *opts)
@@ -215,6 +219,13 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
 		replay.have_squash_onto = 1;
 	}
 
+	if (opts->action == ACTION_CONTINUE) {
+		if (opts->edit >= 0)
+			replay.edit = opts->edit;
+		else if (opts->config_no_edit > 0)
+			replay.edit = 0;
+	}
+
 	return replay;
 }
 
@@ -841,6 +852,11 @@ static int rebase_config(const char *var, const char *value,
 		return 0;
 	}
 
+	if (!strcmp(var, "rebase.noedit")) {
+		opts->config_no_edit = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (!strcmp(var, "rebase.forkpoint")) {
 		opts->fork_point = git_config_bool(var, value) ? -1 : 0;
 		return 0;
@@ -1171,6 +1187,8 @@ int cmd_rebase(int argc,
 			    ACTION_CONTINUE),
 		OPT_CMDMODE(0, "skip", &options.action,
 			    N_("skip current patch and continue"), ACTION_SKIP),
+		OPT_BOOL('e', "edit", &options.edit,
+			 N_("edit the commit message")),
 		OPT_CMDMODE(0, "abort", &options.action,
 			    N_("abort and check out the original branch"),
 			    ACTION_ABORT),
@@ -1311,10 +1329,15 @@ int cmd_rebase(int argc,
 			"which is no longer supported; use 'merges' instead"));
 
 	if (options.action != ACTION_NONE && total_argc != 2) {
-		usage_with_options(builtin_rebase_usage,
-				   builtin_rebase_options);
+		if (options.action != ACTION_CONTINUE ||
+		    options.edit < 0 || total_argc != 3)
+			usage_with_options(builtin_rebase_usage,
+					   builtin_rebase_options);
 	}
 
+	if (options.edit >= 0 && options.action != ACTION_CONTINUE)
+		die(_("--edit and --no-edit can only be used with --continue"));
+
 	if (argc > 2)
 		usage_with_options(builtin_rebase_usage,
 				   builtin_rebase_options);
diff --git a/sequencer.c b/sequencer.c
index 1355a99a09..be2945b12d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2211,6 +2211,25 @@ static int should_edit(struct replay_opts *opts) {
 	return opts->edit;
 }
 
+static int should_edit_rebase_continue(struct replay_opts *opts)
+{
+	if (opts->edit < 0)
+		return 1;
+	return opts->edit;
+}
+
+static void finalize_continue_edit_flags(struct replay_opts *opts,
+					 unsigned int *flags)
+{
+	if (*flags & CLEANUP_MSG)
+		return;
+
+	if (should_edit_rebase_continue(opts))
+		*flags |= EDIT_MSG;
+	else
+		*flags &= ~EDIT_MSG;
+}
+
 static void refer_to_commit(struct repository *r, struct strbuf *msgbuf,
 			    const struct commit *commit,
 			    bool use_commit_reference)
@@ -5281,7 +5300,7 @@ static int commit_staged_changes(struct repository *r,
 				 struct todo_list *todo_list)
 {
 	struct replay_ctx *ctx = opts->ctx;
-	unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
+	unsigned int flags = ALLOW_EMPTY;
 	unsigned int final_fixup = 0, is_clean;
 	struct strbuf rev = STRBUF_INIT;
 	const char *reflog_action = reflog_message(opts, "continue", NULL);
@@ -5446,6 +5465,8 @@ static int commit_staged_changes(struct repository *r,
 		}
 	}
 
+	finalize_continue_edit_flags(opts, &flags);
+
 	if (run_git_commit(final_fixup ? NULL : rebase_path_message(),
 			   reflog_action, opts, flags)) {
 		ret = error(_("could not commit staged changes."));
@@ -5503,6 +5524,12 @@ int sequencer_continue(struct repository *r, struct replay_opts *opts)
 			res = -1;
 			goto release_todo_list;
 		}
+
+		/*
+		 * Command-line --[no-]edit applies only to this
+		 * --continue invocation, not to subsequent picks.
+		 */
+		opts->edit = -1;
 	} else if (!file_exists(get_todo_path(opts)))
 		return continue_single_pick(r, opts);
 	else if ((res = read_populate_todo(r, &todo_list, opts)))
diff --git a/t/t3436-rebase-more-options.sh b/t/t3436-rebase-more-options.sh
index 94671d3c46..c84c6717ab 100755
--- a/t/t3436-rebase-more-options.sh
+++ b/t/t3436-rebase-more-options.sh
@@ -201,6 +201,58 @@ test_expect_success '--ignore-date is an alias for --reset-author-date' '
 	test_atime_is_ignored -2
 '
 
+test_expect_success '--no-edit on continue uses existing commit message' '
+	git checkout commit2 &&
+	test_must_fail git rebase -m --onto commit2^^ commit2^ &&
+	echo resolved >foo &&
+	git add foo &&
+	write_script fail-if-editor-invoked <<-\EOF &&
+	echo editor invoked >&2
+	exit 1
+	EOF
+	GIT_EDITOR=./fail-if-editor-invoked git rebase --continue --no-edit &&
+	git log --format=%s -1 >actual &&
+	echo commit2 >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success '--no-edit cannot be used when starting a rebase' '
+	test_must_fail git rebase --no-edit -m main side 2>err &&
+	test_grep "only be used with --continue" err
+'
+
+test_expect_success 'rebase.noEdit skips editor on continue' '
+	git config rebase.noEdit true &&
+	git checkout commit2 &&
+	test_must_fail git rebase -m --onto commit2^^ commit2^ &&
+	echo resolved >foo &&
+	git add foo &&
+	write_script fail-if-editor-invoked <<-\EOF &&
+	echo editor invoked >&2
+	exit 1
+	EOF
+	GIT_EDITOR=./fail-if-editor-invoked git rebase --continue &&
+	git log --format=%s -1 >actual &&
+	echo commit2 >expect &&
+	test_cmp expect actual
+'
+
+test_expect_success '--edit on continue overrides rebase.noEdit' '
+	git config rebase.noEdit true &&
+	git checkout commit2 &&
+	test_must_fail git rebase -m --onto commit2^^ commit2^ &&
+	echo resolved >foo &&
+	git add foo &&
+	(
+		set_fake_editor &&
+		FAKE_COMMIT_MESSAGE="edited on continue" \
+			git rebase --continue --edit
+	) &&
+	test_write_lines "edited on continue" "" >expect &&
+	git log --format=%B -1 >actual &&
+	test_cmp expect actual
+'
+
 # This must be the last test in this file
 test_expect_success '$EDITOR and friends are unchanged' '
 	test_editor_unchanged
-- 
2.54.0


^ permalink raw reply related

* [PATCH 0/1] rebase: add --[no-]edit to --continue
From: Hugo Sales @ 2026-07-21 14:04 UTC (permalink / raw)
  To: git; +Cc: Hugo Sales

When a rebase stops for conflicts and the user runs `git rebase --continue`, the
merge backend opens $EDITOR so the commit message can be revised. That is often
useful, but not always: sometimes the user only wants to keep the message that
is already there.

This series adds:

- `git rebase --continue --no-edit` to commit without opening an editor
- `rebase.noEdit` to make that the default on continue
- `git rebase --continue --edit` to override `rebase.noEdit`

The command-line flags apply only to the current `--continue` invocation, not to
later picks in the same rebase.

Tests are added in t3436. I also ran all tests locally.

Hugo Sales (1):
  rebase: add --[no-]edit to --continue

 Documentation/config/rebase.adoc |  6 ++++
 Documentation/git-rebase.adoc    | 17 +++++++++--
 builtin/rebase.c                 | 29 ++++++++++++++++--
 sequencer.c                      | 29 +++++++++++++++++-
 t/t3436-rebase-more-options.sh   | 52 ++++++++++++++++++++++++++++++++
 5 files changed, 126 insertions(+), 7 deletions(-)

-- 
2.54.0

^ permalink raw reply

* Re: [PATCH] completion: complete paths for git send-email
From: D. Ben Knoble @ 2026-07-21 12:49 UTC (permalink / raw)
  To: Yury Norov (NVIDIA)
  Cc: git, Thiago Perrotta, Philippe Blain, Junio C Hamano,
	Rubén Justo, Yury Norov, linux-kernel, Codex
In-Reply-To: <20260719134447.381835-1-yury.norov@gmail.com>

On Sun, Jul 19, 2026 at 9:45 AM Yury Norov (NVIDIA)
<yury.norov@gmail.com> wrote:
>
> From: Yury Norov <ynorov@nvidia.com>
>
> git send-email accepts either revisions or paths to patch files, but its
> Bash completion only offers revisions. This prevents patch files from
> being completed. It can also make a prefix such as "0" expand to an
> unrelated hexadecimal ref even when matching 0001-*.patch files exist.
>
> In my Linux tree, an attempt to autocomplete the standard-named patch
> brings a random hashtag:

It is unusual to call this a "hashtag." Perhaps "hash" or "object
name" (or id) based on the glossary and datamodel docs?

>  $ ls 0*
>  0001-bitmap-drop-bitmap_next_set_region.patch
>  $ git send-email 0<Tab>
>  $ git send-email 05c69d298c96703741cac9a5cbbf6c53bd55a6e2
>
> Introduce an append variant of __gitcomp_file() and use it to add
> filesystem candidates after the existing revision candidates.  Keep the
> latter because revisions remain valid send-email arguments.
>
> Add a regression test covering patch files alongside a 40-hex ref.
>
> Assisted-by: Codex <codex@openai.com>
> Signed-off-by: Yury Norov <ynorov@nvidia.com>
> ---
>  contrib/completion/git-completion.bash | 29 +++++++++++++++++++-------
>  t/t9902-completion.sh                  | 12 ++++++++++-
>  2 files changed, 33 insertions(+), 8 deletions(-)
>
> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
> index e87578771..b7017488d 100644
> --- a/contrib/completion/git-completion.bash
> +++ b/contrib/completion/git-completion.bash
> @@ -579,21 +579,18 @@ __gitcomp_file_direct ()
>  }
>
>  # Generates completion reply with compgen from newline-separated possible
> -# completion filenames.
> +# completion filenames by appending them to the existing list of completion
> +# candidates, COMPREPLY.
>  # It accepts 1 to 3 arguments:
>  # 1: List of possible completion filenames, separated by a single newline.
>  # 2: A directory prefix to be added to each possible completion filename
>  #    (optional).
>  # 3: Generate possible completion matches for this word (optional).
> -__gitcomp_file ()
> +__gitcomp_file_append ()
>  {
>         local IFS=$'\n'
>
> -       # XXX does not work when the directory prefix contains a tilde,
> -       # since tilde expansion is not applied.
> -       # This means that COMPREPLY will be empty and Bash default
> -       # completion will be used.
> -       __gitcompadd "$1" "${2-}" "${3-$cur}" ""
> +       __gitcompappend "$1" "${2-}" "${3-$cur}" ""
>
>         # use a hack to enable file mode in bash < 4
>         compopt -o filenames +o nospace 2>/dev/null ||
> @@ -601,6 +598,23 @@ __gitcomp_file ()
>         true
>  }
>
> +# Generates completion reply with compgen from newline-separated possible
> +# completion filenames.
> +# It accepts 1 to 3 arguments:
> +# 1: List of possible completion filenames, separated by a single newline.
> +# 2: A directory prefix to be added to each possible completion filename
> +#    (optional).
> +# 3: Generate possible completion matches for this word (optional).
> +__gitcomp_file ()
> +{
> +       # XXX does not work when the directory prefix contains a tilde,
> +       # since tilde expansion is not applied.
> +       # This means that COMPREPLY will be empty and Bash default
> +       # completion will be used.
> +       COMPREPLY=()
> +       __gitcomp_file_append "$@"
> +}
> +

Curious; the diff itself is much more readable for me when applied
locally (it shows the addition of __gitcomp_file_append and the
replacement of a few lines in __gitcomp_file).

Nonetheless, this follows the pattern established by __gitcompadd and
__gitcompappend, so that part at least looks like it functions as
expected. (I can't comment too much on the code that existed there
already.)

>  # Find the current subcommand for commands that follow the syntax:
>  #
>  #    git <command> <subcommand>
> @@ -2634,6 +2648,7 @@ _git_send_email ()
>                 ;;
>         esac
>         __git_complete_revlist
> +       __gitcomp_file_append "$(compgen -f -- "$cur")"

At least with Bash with compgen, this looks to me like it does append
file names to the COMPREPLY.

But, with the "hack" comment in the modified function, do we also need
to account for older bash? It looks like that comes from 3ffa4df4b2
(completion: add hack to enable file mode in bash < 4, 2013-04-27).
After studying a bit more, that hack is to make Bash do the right
thing during file completion, not to workaround different methods of
generating filenames (unlike Zsh, which has a newer and an older
completion system, Bash's seems relatively stable?).

So, I think this looks good.

>  }
>
>  _git_stage ()
> diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
> index 55dc9eabf..e87827f21 100755
> --- a/t/t9902-completion.sh
> +++ b/t/t9902-completion.sh
> @@ -2777,7 +2777,17 @@ test_expect_success PERL 'send-email' '
>         test_completion "git send-email --val" <<-\EOF &&
>         --validate Z
>         EOF
> -       test_completion "git send-email ma" "main "
> +       test_completion "git send-email ma" "main " &&
> +
> +       git tag 05c69d298c96703741cac9a5cbbf6c53bd55a6e2 &&
> +       test_when_finished "git tag -d 05c69d298c96703741cac9a5cbbf6c53bd55a6e2 &&
> +               rm -f 0001-example.patch 0002-example.patch" &&
> +       touch 0001-example.patch 0002-example.patch &&
> +       test_completion "git send-email 0" <<-\EOF
> +       0001-example.patch
> +       0002-example.patch
> +       05c69d298c96703741cac9a5cbbf6c53bd55a6e2 Z
> +       EOF
>  '
>
>  test_expect_success 'complete files' '
> --
> 2.53.0

Junio commented on the test, so I'll stop here.

Pending a commit message tweak for "hashtag," I'm satisfied enough for

Reviewed-by: D. Ben Knoble <ben.knoble@gmail.com>

(Or feel free to use "Acked-by" if this is not a strong enough review
for you/the project!)

-- 
D. Ben Knoble

^ permalink raw reply

* Re: git config: unintuitive behaviour with --global and --no-includes
From: Hendrik Jaeger @ 2026-07-21 11:53 UTC (permalink / raw)
  To: Jeff King; +Cc: git
In-Reply-To: <20260720125145.GA5100@coredump.intra.peff.net>

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

Hi Jeff

Thanks for your email!

> As for the rationale, it is a mix of backwards compatibility and least-surprise.

To be honest, this reminds me of the XKCD comic with the title "workflow": https://xkcd.com/1172/
The behaviour may be “least-surprise” for the initiated. For everyone new to this, I’d expect it to be as “most-surprising” as it was for me.

Best regards

henk


On Mon, 20 Jul 2026 08:51:45 -0400
Jeff King <peff@peff.net> wrote:

> On Mon, Jul 20, 2026 at 11:34:02AM +0200, Hendrik Jaeger wrote:
> 
> > The manpage says:  
> > > Respect include.*  directives in config files when looking up
> > > values. Defaults to off when a specific file is given (e.g., using
> > > --file, --global, etc) and on when searching all config files.  
> > 
> > IMHO it makes sense the way it is phrased “when a specific file is
> > given” but then seems to turn into non-sense when --global is given as
> > an example. Giving --global is not “giving a specific file” but
> > “restricting to a specific scope”, which may `include` other files.
> > The results seem inconsistent and counterintuitive to me.
> > 
> > Am I misunderstanding anything here?
> > Is this behaviour intended?
> > If it is intended, can someone please explain the rationale behind it? I don’t get it, it seems wrong to me.  
> 
> The behavior you're seeing is intended. Regarding "a specific scope", I
> don't think that's an unreasonable way to think about it. But it's not
> how Git thinks about it, and in particular back when --include was added
> and this behavior was set, "--global" was literally a synonym for
> "--file=$HOME/.gitconfig".
> 
> As for the rationale, it is a mix of backwards compatibility and
> least-surprise. The include functionality was tacked on to the existing
> config parser, and we did not want to surprise anybody who asked for a
> specific file by showing them results for another file. This is
> especially important for reading untrusted input like .gitmodules, but
> also for writing.
> 
> > Regarding the initial issue: I just added --includes to the call in
> > lbmk and it works just fine, so there is no need to address this. I
> > only mentioned it for context to how I got to looking into this
> > behaviour.  
> 
> IMHO lbmk is wrong to be using "--global" in the first place. Looking at
> the source, it is trying to check whether the user has set up their
> identity. But it is not lbmk's business whether you did it in the
> --global config file, or elsewhere! So it should probably just use a
> straight "git config user.name", which will do the same resolution that
> Git will do internally.
> 
> The "--global" was added in their 4a280c62 (.gitcheck: re-write
> entirely. force global config., 2023-08-27), but I don't see any
> rationale given.
> 
> Depending on what they are trying to check, it might be even better
> still for it to use "git var GIT_AUTHOR_IDENT". That will give the
> actual ident Git will derive, including things like checking $EMAIL in
> the environment and so on.
> 
> So if the intent is "will Git come up with some ident", then that is the
> most accurate way to check it. But if the intent is "did the user
> specifically configure Git (because we are worried that values derived
> from GECOS and $EMAIL might not be accurate)", then checking user.*
> specifically is closer to that.
> 
> Though note there is one other hitch, which is that the user can set
> author.* and committer.* as specific variables, since 39ab4d0951
> (config: allow giving separate author and committer idents, 2019-02-04).
> I suspect not many people do that, but that would also be something that
> a config-specific check would have to handle (but "git var" would do
> automatically).
> 
> So I think you might consider sending a bug report to lbmk. Feel free to
> point at this thread, and I'm happy to discuss further with them.
> 
> -Peff

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply

* [PATCH v2 2/2] remote: find tracking branches for URL push destinations
From: Harald Nordgren via GitGitGadget @ 2026-07-21  8:58 UTC (permalink / raw)
  To: git; +Cc: Harald Nordgren, Harald Nordgren
In-Reply-To: <pull.2358.v2.git.git.1784624306.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

Git already accepts a repository URL as branch.<name>.pushRemote and
can push to it. When a configured remote has the same URL, however,
"git status" cannot show that remote's push branch.

This can happen in fork workflows when the original remote is renamed
to "upstream", the fork is added as "origin", and an existing
pushRemote value still contains the fork URL. The URL still points to
the right repository, so pushing works. However, @{push} is unavailable
because Git does not connect the URL to "origin". As a result,
"git status" cannot show the push branch, and an up-to-date push can
leave its local tracking information stale.

When exactly one configured remote has the URL as one of its
remote.<name>.url values, use its fetch refspec to find and refresh the
push branch. Keep the URL as the push destination so the configured
remote's push settings do not change existing behavior. Keep the
current behavior when no remote matches or multiple remotes match.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
 Documentation/config/branch.adoc |   2 +
 Documentation/revisions.adoc     |   3 +
 remote.c                         |  27 +++++++-
 remote.h                         |   2 +
 t/t5505-remote.sh                | 104 +++++++++++++++++++++++++++++++
 transport.c                      |   5 +-
 6 files changed, 141 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/branch.adoc b/Documentation/config/branch.adoc
index a4db9fa5c8..e22b6c846d 100644
--- a/Documentation/config/branch.adoc
+++ b/Documentation/config/branch.adoc
@@ -55,6 +55,8 @@ This option defaults to `never`.
 	repository), you would want to set `remote.pushDefault` to
 	specify the remote to push to for all branches, and use this
 	option to override it for a specific branch.
+	The value may be the name of a configured remote or a repository
+	URL. A URL is used directly as the push destination.
 
 `branch.<name>.merge`::
 	Defines, together with `branch.<name>.remote`, the upstream branch
diff --git a/Documentation/revisions.adoc b/Documentation/revisions.adoc
index 6ea6c7cead..670fc66053 100644
--- a/Documentation/revisions.adoc
+++ b/Documentation/revisions.adoc
@@ -127,6 +127,9 @@ some output processing may assume ref names in UTF-8.
   `git push` were run while `branchname` was checked out (or the current
   `HEAD` if no branchname is specified). Like for '@\{upstream\}', we report
   the remote-tracking branch that corresponds to that branch at the remote.
+  If the push destination is a URL and exactly one configured remote has the
+  same `remote.<name>.url`, '@\{push}' reports the remote-tracking branch for
+  that remote.
 +
 Here's an example to make it more clear:
 +
diff --git a/remote.c b/remote.c
index 0dc36956c3..2e07bd998f 100644
--- a/remote.c
+++ b/remote.c
@@ -1887,13 +1887,38 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
 	return branch->merge[0]->dst;
 }
 
-static char *tracking_for_push_dest(struct repository *repo UNUSED,
+struct remote *repo_remote_for_push_tracking(struct repository *repo,
+					     struct remote *remote)
+{
+	struct remote *first_match = NULL;
+	struct remote_state *remote_state = repo->remote_state;
+
+	if (remote->origin != REMOTE_UNCONFIGURED || remote->url.nr != 1)
+		return remote;
+
+	for (int i = 0; i < remote_state->remotes_nr; i++) {
+		struct remote *candidate = remote_state->remotes[i];
+
+		if (!candidate || candidate == remote ||
+		    !remote_is_configured(candidate, 0) ||
+		    !remote_has_url(candidate, remote->url.v[0]))
+			continue;
+		if (first_match)
+			return remote;
+		first_match = candidate;
+	}
+
+	return first_match ? first_match : remote;
+}
+
+static char *tracking_for_push_dest(struct repository *repo,
 				    struct remote *remote,
 				    const char *refname,
 				    struct strbuf *err)
 {
 	char *ret;
 
+	remote = repo_remote_for_push_tracking(repo, remote);
 	ret = apply_refspecs(&remote->fetch, refname);
 	if (!ret)
 		return error_buf(err,
diff --git a/remote.h b/remote.h
index 72a54d84ad..cca02033b9 100644
--- a/remote.h
+++ b/remote.h
@@ -345,6 +345,8 @@ char *remote_ref_for_branch(struct branch *branch, int for_push);
 
 const char *repo_default_remote(struct repository *repo);
 const char *repo_remote_from_url(struct repository *repo, const char *url);
+struct remote *repo_remote_for_push_tracking(struct repository *repo,
+					     struct remote *remote);
 
 /* returns true if the given branch has merge configuration given. */
 int branch_has_merge_config(struct branch *branch);
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index 6f5e86dede..2c86661294 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -24,6 +24,28 @@ setup_repository () {
 	)
 }
 
+setup_url_pushremote () {
+	rm -rf fork.git client &&
+	git clone --bare one fork.git &&
+	git clone one client &&
+	fork_url="$TRASH_DIRECTORY/fork.git" &&
+	(
+		cd client &&
+		git checkout -b topic --track origin/main &&
+		git commit --allow-empty -m topic-change &&
+		git config push.default current &&
+		git config status.compareBranches "@{upstream} @{push}" &&
+		git config branch.topic.pushRemote "$fork_url" &&
+		git push
+	)
+}
+
+check_status () {
+	git -C client status >actual &&
+	cat >expected &&
+	test_cmp expected actual
+}
+
 tokens_match () {
 	echo "$1" | tr ' ' '\012' | sort | sed -e '/^$/d' >expect &&
 	echo "$2" | tr ' ' '\012' | sort | sed -e '/^$/d' >actual &&
@@ -1018,6 +1040,88 @@ test_expect_success 'rename a remote renames repo remote.pushDefault but keeps g
 	)
 '
 
+test_expect_success 'URL-valued pushRemote without matching remote is not trackable' '
+	setup_url_pushremote &&
+
+	check_status <<-EOF
+	On branch topic
+	Your branch is ahead of ${SQ}origin/main${SQ} by 1 commit.
+	  (use "git push" to publish your local commits)
+
+	nothing to commit, working tree clean
+	EOF
+'
+
+test_expect_success 'adding fork remote makes URL-valued pushRemote trackable' '
+	setup_url_pushremote &&
+
+	(
+		cd client &&
+		git remote rename origin upstream &&
+		git remote add -f origin "$fork_url"
+	) &&
+
+	check_status <<-EOF
+	On branch topic
+	Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
+
+	Your branch is up to date with ${SQ}origin/topic${SQ}.
+
+	nothing to commit, working tree clean
+	EOF
+'
+
+test_expect_success 'up-to-date URL push refreshes stale tracking branch' '
+	setup_url_pushremote &&
+	(
+		cd client &&
+		git remote rename origin upstream &&
+		git remote add -f origin "$fork_url" &&
+		git commit --allow-empty -m another-topic-change &&
+		git -C ../fork.git fetch ../client topic:topic
+	) &&
+
+	check_status <<-EOF &&
+	On branch topic
+	Your branch is ahead of ${SQ}upstream/main${SQ} by 2 commits.
+
+	Your branch is ahead of ${SQ}origin/topic${SQ} by 1 commit.
+	  (use "git push" to publish your local commits)
+
+	nothing to commit, working tree clean
+	EOF
+
+	git -C client push >actual 2>&1 &&
+	test_grep "Everything up-to-date" actual &&
+
+	check_status <<-EOF
+	On branch topic
+	Your branch is ahead of ${SQ}upstream/main${SQ} by 2 commits.
+
+	Your branch is up to date with ${SQ}origin/topic${SQ}.
+
+	nothing to commit, working tree clean
+	EOF
+'
+
+test_expect_success 'duplicate remote URL leaves URL-valued pushRemote ambiguous' '
+	setup_url_pushremote &&
+	(
+		cd client &&
+		git remote rename origin upstream &&
+		git remote add -f origin "$fork_url" &&
+		git remote add duplicate "$fork_url"
+	) &&
+
+	check_status <<-EOF
+	On branch topic
+	Your branch is ahead of ${SQ}upstream/main${SQ} by 1 commit.
+	  (use "git push" to publish your local commits)
+
+	nothing to commit, working tree clean
+	EOF
+'
+
 test_expect_success 'rename handles remote without fetch refspec' '
 	git clone --bare one no-refspec.git &&
 	# confirm assumption that bare clone does not create refspec
diff --git a/transport.c b/transport.c
index fc144f0aed..30a4ab2cd5 100644
--- a/transport.c
+++ b/transport.c
@@ -1553,8 +1553,11 @@ int transport_push(struct repository *r,
 	if (!(flags & (TRANSPORT_PUSH_DRY_RUN |
 		       TRANSPORT_RECURSE_SUBMODULES_ONLY))) {
 		struct ref *ref;
+		struct remote *tracking_remote = repo_remote_for_push_tracking(
+			r, transport->remote);
+
 		for (ref = remote_refs; ref; ref = ref->next)
-			transport_update_tracking_ref(transport->remote, ref, verbose);
+			transport_update_tracking_ref(tracking_remote, ref, verbose);
 	}
 
 	if (porcelain && !push_ret)
-- 
gitgitgadget

^ permalink raw reply related

* [PATCH v2 1/2] remote: pass repository to push tracking helper
From: Harald Nordgren via GitGitGadget @ 2026-07-21  8:58 UTC (permalink / raw)
  To: git; +Cc: Harald Nordgren, Harald Nordgren
In-Reply-To: <pull.2358.v2.git.git.1784624306.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

The next commit needs tracking_for_push_dest() to inspect the
repository's configured remotes. Pass the repository through the
existing callers and mark the new parameter as unused.

No change in behavior.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
 remote.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/remote.c b/remote.c
index b17648d6ef..0dc36956c3 100644
--- a/remote.c
+++ b/remote.c
@@ -1887,7 +1887,8 @@ const char *branch_get_upstream(struct branch *branch, struct strbuf *err)
 	return branch->merge[0]->dst;
 }
 
-static char *tracking_for_push_dest(struct remote *remote,
+static char *tracking_for_push_dest(struct repository *repo UNUSED,
+				    struct remote *remote,
 				    const char *refname,
 				    struct strbuf *err)
 {
@@ -1925,13 +1926,13 @@ static char *branch_get_push_1(struct repository *repo,
 					 _("push refspecs for '%s' do not include '%s'"),
 					 remote->name, branch->name);
 
-		ret = tracking_for_push_dest(remote, dst, err);
+		ret = tracking_for_push_dest(repo, remote, dst, err);
 		free(dst);
 		return ret;
 	}
 
 	if (remote->mirror)
-		return tracking_for_push_dest(remote, branch->refname, err);
+		return tracking_for_push_dest(repo, remote, branch->refname, err);
 
 	switch (push_default) {
 	case PUSH_DEFAULT_NOTHING:
@@ -1939,7 +1940,7 @@ static char *branch_get_push_1(struct repository *repo,
 
 	case PUSH_DEFAULT_MATCHING:
 	case PUSH_DEFAULT_CURRENT:
-		return tracking_for_push_dest(remote, branch->refname, err);
+		return tracking_for_push_dest(repo, remote, branch->refname, err);
 
 	case PUSH_DEFAULT_UPSTREAM:
 		return xstrdup_or_null(branch_get_upstream(branch, err));
@@ -1953,7 +1954,7 @@ static char *branch_get_push_1(struct repository *repo,
 			up = branch_get_upstream(branch, err);
 			if (!up)
 				return NULL;
-			cur = tracking_for_push_dest(remote, branch->refname, err);
+			cur = tracking_for_push_dest(repo, remote, branch->refname, err);
 			if (!cur)
 				return NULL;
 			if (strcmp(cur, up)) {
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 0/2] remote: renamed remote push tracking
From: Harald Nordgren via GitGitGadget @ 2026-07-21  8:58 UTC (permalink / raw)
  To: git; +Cc: Harald Nordgren
In-Reply-To: <pull.2358.git.git.1784538618.gitgitgadget@gmail.com>

Keep git status showing the push branch after remotes are renamed by finding
the configured remote with the same URL.

Changes in v3:

 * Revamp commit messages to clarify motivation.

Changes in v2:

 * Clarify that URL push destinations already work and that this change only
   restores their tracking information.
 * Document URL values for branch.<name>.pushRemote and their @{push}
   behavior.

Harald Nordgren (2):
  remote: pass repository to push tracking helper
  remote: find tracking branches for URL push destinations

 Documentation/config/branch.adoc |   2 +
 Documentation/revisions.adoc     |   3 +
 remote.c                         |  36 +++++++++--
 remote.h                         |   2 +
 t/t5505-remote.sh                | 104 +++++++++++++++++++++++++++++++
 transport.c                      |   5 +-
 6 files changed, 146 insertions(+), 6 deletions(-)


base-commit: 48bbf81c29ca9a4479ec7850fe206518682cdb2f
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2358%2FHaraldNordgren%2Fremote-resolve-url-push-tracking-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2358/HaraldNordgren/remote-resolve-url-push-tracking-v2
Pull-Request: https://github.com/git/git/pull/2358

Range-diff vs v1:

 1:  fc70895732 ! 1:  b1ac49de87 remote: pass repository to push tracking helper
     @@ Metadata
       ## Commit message ##
          remote: pass repository to push tracking helper
      
     -    The push tracking helper currently only needs the push remote. However,
     -    resolving a URL-valued remote requires access to the repository's list
     -    of configured remotes.
     +    The next commit needs tracking_for_push_dest() to inspect the
     +    repository's configured remotes. Pass the repository through the
     +    existing callers and mark the new parameter as unused.
      
     -    Pass the repository through the existing callers and mark the parameter
     -    as unused for now. This prepares the helper for that lookup without
     -    changing its behavior.
     +    No change in behavior.
      
          Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
      
 2:  ff645b2159 ! 2:  6e924a7fec remote: resolve URL-valued push tracking remotes
     @@ Metadata
      Author: Harald Nordgren <haraldnordgren@gmail.com>
      
       ## Commit message ##
     -    remote: resolve URL-valued push tracking remotes
     +    remote: find tracking branches for URL push destinations
      
     -    A branch may name its push destination with a URL instead of a
     -    configured remote. This is useful in fork workflows, where the original
     -    remote is renamed to "upstream", the fork is added as "origin", and an
     -    existing branch.<name>.pushRemote continues to contain the fork URL.
     +    Git already accepts a repository URL as branch.<name>.pushRemote and
     +    can push to it. When a configured remote has the same URL, however,
     +    "git status" cannot show that remote's push branch.
      
     -    Git can still push through the anonymous remote created for that URL.
     -    However, the anonymous remote has no fetch refspec. Git therefore cannot
     -    resolve @{push} to origin/<branch> or update that remote-tracking branch
     -    after a push. The push can succeed, or report that everything is up to
     -    date, while status continues to compare against a stale tracking ref or
     -    cannot show the push branch at all.
     +    This can happen in fork workflows when the original remote is renamed
     +    to "upstream", the fork is added as "origin", and an existing
     +    pushRemote value still contains the fork URL. The URL still points to
     +    the right repository, so pushing works. However, @{push} is unavailable
     +    because Git does not connect the URL to "origin". As a result,
     +    "git status" cannot show the push branch, and an up-to-date push can
     +    leave its local tracking information stale.
      
     -    A uniquely matching configured remote already provides the missing
     -    mapping. Use its fetch refspec when resolving the push tracking branch
     -    and when updating tracking refs after a push. This changes neither the
     -    push destination nor configuration. Keep the existing behavior when no
     -    remote matches or multiple remotes share the URL, since either case is
     -    ambiguous.
     +    When exactly one configured remote has the URL as one of its
     +    remote.<name>.url values, use its fetch refspec to find and refresh the
     +    push branch. Keep the URL as the push destination so the configured
     +    remote's push settings do not change existing behavior. Keep the
     +    current behavior when no remote matches or multiple remotes match.
      
          Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
      
     + ## Documentation/config/branch.adoc ##
     +@@ Documentation/config/branch.adoc: This option defaults to `never`.
     + 	repository), you would want to set `remote.pushDefault` to
     + 	specify the remote to push to for all branches, and use this
     + 	option to override it for a specific branch.
     ++	The value may be the name of a configured remote or a repository
     ++	URL. A URL is used directly as the push destination.
     + 
     + `branch.<name>.merge`::
     + 	Defines, together with `branch.<name>.remote`, the upstream branch
     +
       ## Documentation/revisions.adoc ##
      @@ Documentation/revisions.adoc: some output processing may assume ref names in UTF-8.
         `git push` were run while `branchname` was checked out (or the current
         `HEAD` if no branchname is specified). Like for '@\{upstream\}', we report
         the remote-tracking branch that corresponds to that branch at the remote.
     -+  If the push remote is specified as a URL, the fetch refspec of a uniquely
     -+  matching configured remote is used to find and update the remote-tracking
     -+  branch.
     ++  If the push destination is a URL and exactly one configured remote has the
     ++  same `remote.<name>.url`, '@\{push}' reports the remote-tracking branch for
     ++  that remote.
       +
       Here's an example to make it more clear:
       +

-- 
gitgitgadget

^ permalink raw reply

* Re: [PATCH v6 00/10] commit-reach: terminate merge-base walk when one side is exhausted
From: Kristofer Karlsson @ 2026-07-21  8:45 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Kristofer Karlsson via GitGitGadget, git, Derrick Stolee,
	Elijah Newren, René Scharfe, SZEDER Gábor
In-Reply-To: <xmqqse5en8wz.fsf@gitster.g>

On Sun, 19 Jul 2026 at 20:14, Junio C Hamano <gitster@pobox.com> wrote:
>
> In any case, we really need to get somebody take a look at these
> patches to move them forward.  Any takers?
>
> Thanks.

Yes it seems we lost some momentum here. I am not personally
stressed about it but it is of course better to reduce the number
of topics in-flight.

If the patch series is getting too large perhaps I need to shrink
it down in size or complexity, but I am not sure if that is wanted
and if so, in which aspect it should be simplified.

Some alternatives:
- Skip the final commit that cleans up the date ordering fallback.
  Currently just a nice win, but it could be submitted separately.
- Skip the extra test helper to get nicer assertion failure
  messages. It was helpful during development but is not strictly
  required.
- Squash together some of the test commits to reduce the number of
  patches.
- Squash together some of the logic changes to jump more directly
  to the desired end state -- though I am not sure if this would
  actually make the review process simpler.

But perhaps this is simply the time of year where people take
more vacation and are thus spending less time on code reviews.

Thanks,
Kristofer

^ permalink raw reply

* [PATCH v2] userdiff: add support for Swift
From: Shlok Kulshreshtha @ 2026-07-21  6:57 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Johannes Sixt, D . Ben Knoble, René Scharfe,
	Eric Sunshine, Scott L . Burson, Shlok Kulshreshtha
In-Reply-To: <20260717140232.6722-1-diy2903@gmail.com>

Add a built-in userdiff driver for the Swift programming language so that
diff hunk headers and word diffs work out of the box for ".swift" files.

The funcname pattern is built for Swift's own declaration grammar: an
optional run of attributes ("@objc", "@available(iOS 13, *)", ...),
followed by an optional run of lowercase modifiers ("public", "static",
"final", ...), followed by a declaration keyword (func, class, struct,
enum, protocol, extension, actor, init, deinit, subscript). The keyword
is followed by a boundary that allows whitespace, "(" (init/subscript),
"?" or "!" (failable init), or "<" (generics), while still acting as a
word boundary so e.g. "initialize(" does not match.

The word regex recognizes Swift identifiers, hexadecimal, octal, binary,
integer and floating-point literals, and the language's operators.

Signed-off-by: Shlok Kulshreshtha <diy2903@gmail.com>
---
v2, addressing Johannes Sixt's review of v1
(<2a3a73c5-5e90-44a3-bf6a-6e98ce5e5a59@kdbg.org>).  Changes since v1:

 - t4018/swift-{init,failable-init,generic-subscript}: "RIGHT" now
   appears only once, on the declaration line, so the expected header is
   unambiguous.
 - word regex: dropped the redundant "?" after the single-character
   operator class.  Single characters are already covered by the
   "|[^[:space:]]" fallback that the PATTERNS macro appends, so only the
   two-character forms need to be spelled out.

(A couple of Hannes's other suggestions I kept as-is; I have explained
the reasoning in a reply to his review.)

Some coverage evidence beyond the t4018 fixtures:

 - Grammar: a test over every declaration form in Swift's grammar
   summary (26 forms -- func/class/struct/enum/protocol/extension/actor,
   init incl. "init?"/"init!"/generic, deinit, subscript incl. generic,
   operator methods, stacked modifiers, inline attributes with and
   without arguments, "where" clauses, multi-line signatures) -- all 26
   resolve to the correct declaration.

 - Corpus: run over the last 200 commits touching *.swift in seven
   stylistically different projects (Alamofire, apple/
   swift-argument-parser, vapor, Kingfisher, RxSwift, SnapKit,
   pointfreeco/swift-composable-architecture): of 20454 hunks, 15310
   produced a header and 15296 (99.9%) named a real declaration.  The
   empty-header hunks are changes with no enclosing declaration (file
   comment blocks, imports, Package.swift, top-level code); sampling
   found no change inside a declaration that failed to get a header.
   The handful of non-declaration headers are the selective-import form
   ("import class Foundation.Bundle"), which reads "import" as a
   modifier; rare and low-harm, and I can exclude it in a follow-up if
   preferred.

 Documentation/gitattributes.adoc  |  2 ++
 t/t4018/swift-actor               |  5 +++++
 t/t4018/swift-attribute-with-args |  7 +++++++
 t/t4018/swift-class               |  5 +++++
 t/t4018/swift-enum                |  5 +++++
 t/t4018/swift-extension           |  5 +++++
 t/t4018/swift-failable-init       |  7 +++++++
 t/t4018/swift-func                |  5 +++++
 t/t4018/swift-generic-subscript   |  7 +++++++
 t/t4018/swift-init                |  7 +++++++
 t/t4018/swift-inline-attribute    |  7 +++++++
 t/t4018/swift-modifiers           |  4 ++++
 t/t4018/swift-protocol            |  5 +++++
 t/t4018/swift-struct              |  5 +++++
 userdiff.c                        | 10 ++++++++++
 15 files changed, 86 insertions(+)
 create mode 100644 t/t4018/swift-actor
 create mode 100644 t/t4018/swift-attribute-with-args
 create mode 100644 t/t4018/swift-class
 create mode 100644 t/t4018/swift-enum
 create mode 100644 t/t4018/swift-extension
 create mode 100644 t/t4018/swift-failable-init
 create mode 100644 t/t4018/swift-func
 create mode 100644 t/t4018/swift-generic-subscript
 create mode 100644 t/t4018/swift-init
 create mode 100644 t/t4018/swift-inline-attribute
 create mode 100644 t/t4018/swift-modifiers
 create mode 100644 t/t4018/swift-protocol
 create mode 100644 t/t4018/swift-struct

diff --git a/Documentation/gitattributes.adoc b/Documentation/gitattributes.adoc
index bd76167a45..9fea75f96f 100644
--- a/Documentation/gitattributes.adoc
+++ b/Documentation/gitattributes.adoc
@@ -914,6 +914,8 @@ patterns are available:
 - `scheme` suitable for source code in most Lisp dialects,
   including Scheme, Emacs Lisp, Common Lisp, and Clojure.
 
+- `swift` suitable for source code in the Swift language.
+
 - `tex` suitable for source code for LaTeX documents.
 
 
diff --git a/t/t4018/swift-actor b/t/t4018/swift-actor
new file mode 100644
index 0000000000..e4852f40a7
--- /dev/null
+++ b/t/t4018/swift-actor
@@ -0,0 +1,5 @@
+actor RIGHT {
+    let a = 1
+    // a comment
+    let b = ChangeMe
+}
diff --git a/t/t4018/swift-attribute-with-args b/t/t4018/swift-attribute-with-args
new file mode 100644
index 0000000000..22b1ee32f1
--- /dev/null
+++ b/t/t4018/swift-attribute-with-args
@@ -0,0 +1,7 @@
+struct View {
+    @available(iOS 13, *) public func RIGHT() {
+        let a = 1
+        // a comment
+        print(ChangeMe)
+    }
+}
diff --git a/t/t4018/swift-class b/t/t4018/swift-class
new file mode 100644
index 0000000000..c3a9336027
--- /dev/null
+++ b/t/t4018/swift-class
@@ -0,0 +1,5 @@
+class RIGHT {
+    let a = 1
+    // a comment
+    let b = ChangeMe
+}
diff --git a/t/t4018/swift-enum b/t/t4018/swift-enum
new file mode 100644
index 0000000000..0a84302993
--- /dev/null
+++ b/t/t4018/swift-enum
@@ -0,0 +1,5 @@
+enum RIGHT {
+    case first
+    // a comment
+    case ChangeMe
+}
diff --git a/t/t4018/swift-extension b/t/t4018/swift-extension
new file mode 100644
index 0000000000..cbc18ab6ef
--- /dev/null
+++ b/t/t4018/swift-extension
@@ -0,0 +1,5 @@
+extension RIGHT {
+    static let a = 1
+    // a comment
+    static let b = ChangeMe
+}
diff --git a/t/t4018/swift-failable-init b/t/t4018/swift-failable-init
new file mode 100644
index 0000000000..4bbd6217c9
--- /dev/null
+++ b/t/t4018/swift-failable-init
@@ -0,0 +1,7 @@
+class Bar {
+    init?(RIGHT: Int) {
+        let x = 0
+        // a comment
+        print(ChangeMe)
+    }
+}
diff --git a/t/t4018/swift-func b/t/t4018/swift-func
new file mode 100644
index 0000000000..1fecae0911
--- /dev/null
+++ b/t/t4018/swift-func
@@ -0,0 +1,5 @@
+func RIGHT(x: Int) -> Int {
+    let y = x
+    // a comment
+    return ChangeMe
+}
diff --git a/t/t4018/swift-generic-subscript b/t/t4018/swift-generic-subscript
new file mode 100644
index 0000000000..423cb58941
--- /dev/null
+++ b/t/t4018/swift-generic-subscript
@@ -0,0 +1,7 @@
+struct Container {
+    subscript<RIGHT>(index: Int) -> Int {
+        let a = 0
+        // a comment
+        return ChangeMe
+    }
+}
diff --git a/t/t4018/swift-init b/t/t4018/swift-init
new file mode 100644
index 0000000000..dc7a298f38
--- /dev/null
+++ b/t/t4018/swift-init
@@ -0,0 +1,7 @@
+class Foo {
+    init(RIGHT: Int) {
+        let x = 0
+        // a comment
+        print(ChangeMe)
+    }
+}
diff --git a/t/t4018/swift-inline-attribute b/t/t4018/swift-inline-attribute
new file mode 100644
index 0000000000..2374c4b603
--- /dev/null
+++ b/t/t4018/swift-inline-attribute
@@ -0,0 +1,7 @@
+class Service {
+    @objc func RIGHT() {
+        let path = "/api"
+        // a comment
+        log(ChangeMe)
+    }
+}
diff --git a/t/t4018/swift-modifiers b/t/t4018/swift-modifiers
new file mode 100644
index 0000000000..9d80685a78
--- /dev/null
+++ b/t/t4018/swift-modifiers
@@ -0,0 +1,4 @@
+public static func RIGHT() -> Int {
+    // a comment
+    return ChangeMe
+}
diff --git a/t/t4018/swift-protocol b/t/t4018/swift-protocol
new file mode 100644
index 0000000000..07c39ec2a3
--- /dev/null
+++ b/t/t4018/swift-protocol
@@ -0,0 +1,5 @@
+protocol RIGHT {
+    var first: Int { get }
+    // a comment
+    var second: ChangeMe { get }
+}
diff --git a/t/t4018/swift-struct b/t/t4018/swift-struct
new file mode 100644
index 0000000000..e399ed7759
--- /dev/null
+++ b/t/t4018/swift-struct
@@ -0,0 +1,5 @@
+struct RIGHT {
+    let a = 1
+    // a comment
+    let b = ChangeMe
+}
diff --git a/userdiff.c b/userdiff.c
index b5412e6bc3..7129bf1482 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -362,6 +362,16 @@ PATTERNS("scheme",
 	 "\\|([^|\\\\]|\\\\.)*\\|"
 	 /* All other words should be delimited by spaces or parentheses. */
 	 "|([^][)(}{ \t])+"),
+PATTERNS("swift",
+	 "^[ \t]*((@[A-Za-z_][A-Za-z0-9_]*(\\([^()]*\\))?[ \t]+)*([a-z]+[ \t]+)*(func|init|deinit|subscript|class|struct|enum|protocol|extension|actor)[ \t(?!<].*)$",
+	 /* -- */
+	 "[a-zA-Z_][a-zA-Z0-9_]*"
+	 /* hexadecimal, octal, and binary literals */
+	 "|0[xX][0-9a-fA-F_]+|0[oO][0-7_]+|0[bB][01_]+"
+	 /* integers and floating-point numbers */
+	 "|[0-9][0-9_]*([.][0-9_]+)?([eE][-+]?[0-9]+)?"
+	 /* unary and binary operators */
+	 "|[-+*/%<>=!&|^~?]=|&&|\\|\\||<<=?|>>=?|\\?\\?|\\.\\.[.<]|->"),
 PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
 	 "\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
 { .name = "default", .binary = -1 },

Range-diff against v1:
1:  1e7e199355 ! 1:  af48611565 userdiff: add support for Swift
    @@ t/t4018/swift-failable-init (new)
     @@
     +class Bar {
     +    init?(RIGHT: Int) {
    -+        let value = RIGHT
    ++        let x = 0
     +        // a comment
     +        print(ChangeMe)
     +    }
    @@ t/t4018/swift-func (new)
      ## t/t4018/swift-generic-subscript (new) ##
     @@
     +struct Container {
    -+    subscript<RIGHT>(index: RIGHT) -> Int {
    ++    subscript<RIGHT>(index: Int) -> Int {
     +        let a = 0
     +        // a comment
     +        return ChangeMe
    @@ t/t4018/swift-init (new)
     @@
     +class Foo {
     +    init(RIGHT: Int) {
    -+        let value = RIGHT
    ++        let x = 0
     +        // a comment
     +        print(ChangeMe)
     +    }
    @@ userdiff.c: PATTERNS("scheme",
     +	 /* integers and floating-point numbers */
     +	 "|[0-9][0-9_]*([.][0-9_]+)?([eE][-+]?[0-9]+)?"
     +	 /* unary and binary operators */
    -+	 "|[-+*/%<>=!&|^~?]=?|&&|\\|\\||<<=?|>>=?|\\?\\?|\\.\\.[.<]|->"),
    ++	 "|[-+*/%<>=!&|^~?]=|&&|\\|\\||<<=?|>>=?|\\?\\?|\\.\\.[.<]|->"),
      PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
      	 "\\\\[a-zA-Z@]+|\\\\.|([a-zA-Z0-9]|[^\x01-\x7f])+"),
      { .name = "default", .binary = -1 },
-- 
2.52.0


^ permalink raw reply related

* Re: Performance regression in connectivity check during receive-pack (git 2.54)
From: Taylor Blau @ 2026-07-21  5:05 UTC (permalink / raw)
  To: Jeff King; +Cc: Wolfgang Kritzinger, Patrick Steinhardt, git
In-Reply-To: <20260721035733.GA581473@coredump.intra.peff.net>

On Mon, Jul 20, 2026 at 11:57:33PM -0400, Jeff King wrote:
> On Tue, Jul 21, 2026 at 03:17:23PM +1200, Wolfgang Kritzinger wrote:
>
> > `strace` shows that after a push, 2.54 does a failing open() of
> > of numerous loose objects -- once in the quarantine (incoming)
> > directory and once in the main object store -- before finding it
> > in a pack:
> >
> > openat(".../objects/tmp_objdir-incoming-XXXX/ed/58..", O_RDONLY) = ENOENT
> > openat(".../objects/ed/58..", O_RDONLY) = ENOENT
>
> Interesting. Here's a smaller reproduction recipe that shows the issue:
>
>   # clone of git.git, or any other non-trivial repo; it should be mostly
>   # packed
>   src=/path/to/git
>
>   git init empty
>   export GIT_ALTERNATE_OBJECT_DIRECTORIES=$src/.git/objects
>   strace -fe openat \
>     git -C empty rev-list --objects $(git -C $src rev-parse HEAD) >/dev/null
>
> In v2.50, we see almost no loose object open calls, because we check the
> pack first. But in v2.54, we see tons of them.
>
> > 2.50 does not do this. In most customer deployments of Bitbucket,
> > the Git data lives on an NFS share. The extra latency on NFS makes
> > this process of checking for non-existent loose objects take too
> > long, the push essentially hangs at the "Checking connectivity" step.
>
> Yeah, I can imagine. But even on a fast filesystem, we definitely want
> to avoid all of those syscalls. Replacing "strace" above with a timing
> harness, even on a system with fast syscalls and a warm cache, the v2.54
> version is ~12% slower.
>
> > I believe this new behavior was introduced in the recent object
> > database rework. After using bisect, I belive the problem can be
> > traced back to commit 8384cbcb4c.


> Hmm, my bisect ended up at a593373b09 (packfile: refactor
> `find_pack_entry()` to work on the packfile store, 2026-01-09), which is
> nearby. I'm not sure if it might depend on other factors (e.g., presence
> of commit graphs, midx, etc) or my reproduction is not exactly like
> yours, or if one of us messed up bisection.
>
> +cc Patrick as the author of both commits.

I think that both bisection results are equally valid for different
reasons.

Before 8384cbcb4c, 'find_pack_entry()' did

    packfile_store_prepare(r->objects->sources->packfiles);

, then tried each of the stores in order to first see if (1) a MIDX was
available to locate the object in some pack, or (2) failing that, if
there exists some non-MIDX'd pack which could do the same.

Worth noting is that 'packfile_store_prepare()' effectively did:

    for (s = store->source->odb->sources; s; s = s->next) {
        prepare_multi_pack_index_one(s);
        prepare_packed_git_one(s);
    }

Thus preparing the first store also prepared every alternate store,
enabling 'find_pack_entry()' to search through all store's MIDX and
pack lists/sources.

8384cbcb4c changes this such that 'packfile_store_prepare()' now only
prepares its owning source:

    prepare_multi_pack_index_one(store->source);
    prepare_packed_git_one(store->source);

, which is reasonable, but 'find_pack_entry()' still loops over all
sources starting from 'r->objects->sources' and calls the function
'packfile_store_prepare()'. But! It calls that function over the same
argument each time, like so:

    for (source = r->objects->sources; source; source = source->next) {
        packfile_store_prepare(r->objects->sources->packfiles);
        if (source->midx && fill_midx_entry(source->midx, oid, e))
            return 1;
    }

So we never prepare the packfile store from other sources!

In Wolfgang's case, if we have an quarantine store followed by the main
object store, our lookup order will be:

 1. prepare the quarantine object store
 2. search packs in the quarantine object store
 3. search packs in the main object store (which will fail, since this
    list is guaranteed to be empty since we never called
    'packfile_store_prepare()')
 4. search loose objects in the quarantine object store
 5. search loose objects in the main object store
 6. haven't found anything, so we must reprepare
 7. search packs in the main object store, which will now succeed, as
    the previous reprepare called 'packfile_store_prepare()' on the main
    object store's packfile source.

Commit a593373b09 changes things, since it makes 'find_pack_entry()' no
longer operate over the entire repository, but over a single store.
Before searching that store, it prepares it, like so:

    static int find_pack_entry(struct packfile_store *store,
                               const struct object_id *oid,
                               sturct pack_entry *e)
    {
        struct packfile_list_entry *l;

        packfile_store_prepare(store);
        if (store->source->midx && fill_midx_entry(...))
            return 1;

        for (l = store->packs.head; l; l = l->next) {
            struct packed_git *p = l->pack;
            if (!p->multi_pack_index && fill_pack_entry(oid, e, p)) {
                /* ... */
                return 1;
            }
        }

        return 0;
    }

So commit a593373b09 indeed squashes the bug introduced by 8384cbcb4c,
and when lookup reaches the main store, it prepares the main store
correctly.

But a593373b09 also changes the lookup order, because the caller in
'do_oid_object_info_extended()` already loops over sources!

    static int do_oid_object_info_extended(struct object_database *odb,
                                           const struct object_id *oid,
                                           struct object_info *oi, unsigned flags)
    {
        /* replace objects, cached lookups, etc., ... */

        odb_prepare_alterantes(odb);

        while (1) {
            struct odb_source *source;

            for (source = odb->sources; source; source = source->next) {
                if (!packfile_store_read_object_info(source->packfiles,
                                                     real, oi, flags) ||
                    !odb_source_loose_read_object_info(source, real, oi,
                                                       flags))
                    return 0;
            }
        }
    }

Before a593373b09, that call to 'packfile_store_read_object_info()'
looped over all sources, since it still called 'find_pack_entry()'.

In other words, prior to a593373b09, the lookup proceeded like so:

 1. search packfiles in quarantine
 2. search packfiles in the main object store
 3. search loose objects in quarantine
 4. search loose objects in the main object store

But a593373b09 changes that to instead proceed store-by-store, as
follows:

 1. search packfiles in quarantine
 2. search loose objects in quarantine
 3. search packfiles in the main object store
 4. search loose objects in the main object store

So even with all object sources prepared, every object found in a later
source pays a failed loose object lookup in an earlier one, which I
believe matches what Peff strace'd above.

So both bisections make sense. If the later store has not been prepared
yet, commit 8384cbcb4c is where Git first fails to see its packs and
falls through to loose object checks. If the stores are already
prepared, that problem does not show up, and a593373b09 is where Git
first starts checking loose objects in an earlier source before looking
in a later source's packs.

I think that something like the following (untested) would fix the
immediate issue:

--- 8< ---
diff --git a/odb.c b/odb.c
index cf6e7938c0..aeb2915f0f 100644
--- a/odb.c
+++ b/odb.c
@@ -568,9 +568,28 @@ static int do_oid_object_info_extended(struct object_database *odb,
 	while (1) {
 		struct odb_source *source;

-		for (source = odb->sources; source; source = source->next)
-			if (!odb_source_read_object_info(source, real, oi, flags))
+		/*
+		 * Check all packed sources before trying loose ones. A loose
+		 * miss requires a filesystem lookup, and receive-pack's
+		 * quarantine source makes the main object directory an
+		 * alternate.
+		 */
+		for (source = odb->sources; source; source = source->next) {
+			struct odb_source_files *files =
+				odb_source_files_downcast(source);
+
+			if (!odb_source_read_object_info(&files->packed->base,
+							 real, oi, flags))
 				return 0;
+		}
+		for (source = odb->sources; source; source = source->next) {
+			struct odb_source_files *files =
+				odb_source_files_downcast(source);
+
+			if (!odb_source_read_object_info(&files->loose->base,
+							 real, oi, flags))
+				return 0;
+		}

 		/*
 		 * When the object hasn't been found we try a second read and
--- >8 ---

But...

> I'm not sure of the correct fix. This is working against the whole "odb
> sources are independent and abstract" refactoring that a593373b09 was
> going for. But I think it's an important optimization. I guess the
> abstract version would be that each source has "fast" and "slow" lookups
> or something like that, and we check all fast ones before slow ones. But
> that is pretty gross.

...that fix is breaking the very abstraction that the pluggable-ODB
effort is trying to create in the first place, at least in my
understanding of the project's goals.

> I'll leave it to Patrick to ponder further. I haven't really been paying
> a lot of attention to the odb refactoring.

I am genuinely not sure what the right path forward here is, given that
I do not have a super firm understanding of all of the refactoring that
has taken place here. I would be likewise eager to hear from Patrick or
others with thoughts on how to resolve this.

Thanks,
Taylor

^ permalink raw reply related

* Re: Performance regression in connectivity check during receive-pack (git 2.54)
From: Jeff King @ 2026-07-21  3:57 UTC (permalink / raw)
  To: Wolfgang Kritzinger; +Cc: Patrick Steinhardt, git
In-Reply-To: <CAFXJcxvpKHoVDwE5mBOd=w-A5vPdUmehqr8SHLUD7qv1qB00rA@mail.gmail.com>

On Tue, Jul 21, 2026 at 03:17:23PM +1200, Wolfgang Kritzinger wrote:

> `strace` shows that after a push, 2.54 does a failing open() of
> of numerous loose objects -- once in the quarantine (incoming)
> directory and once in the main object store -- before finding it
> in a pack:
> 
> openat(".../objects/tmp_objdir-incoming-XXXX/ed/58..", O_RDONLY) = ENOENT
> openat(".../objects/ed/58..", O_RDONLY) = ENOENT

Interesting. Here's a smaller reproduction recipe that shows the issue:

  # clone of git.git, or any other non-trivial repo; it should be mostly
  # packed
  src=/path/to/git

  git init empty
  export GIT_ALTERNATE_OBJECT_DIRECTORIES=$src/.git/objects
  strace -fe openat \
    git -C empty rev-list --objects $(git -C $src rev-parse HEAD) >/dev/null

In v2.50, we see almost no loose object open calls, because we check the
pack first. But in v2.54, we see tons of them.

> 2.50 does not do this. In most customer deployments of Bitbucket,
> the Git data lives on an NFS share. The extra latency on NFS makes
> this process of checking for non-existent loose objects take too
> long, the push essentially hangs at the "Checking connectivity" step.

Yeah, I can imagine. But even on a fast filesystem, we definitely want
to avoid all of those syscalls. Replacing "strace" above with a timing
harness, even on a system with fast syscalls and a warm cache, the v2.54
version is ~12% slower.

> I believe this new behavior was introduced in the recent object
> database rework. After using bisect, I belive the problem can be
> traced back to commit 8384cbcb4c.

Hmm, my bisect ended up at a593373b09 (packfile: refactor
`find_pack_entry()` to work on the packfile store, 2026-01-09), which is
nearby. I'm not sure if it might depend on other factors (e.g., presence
of commit graphs, midx, etc) or my reproduction is not exactly like
yours, or if one of us messed up bisection.

+cc Patrick as the author of both commits.

> I don't know the codebase well, but from what I can see is that
> the order in which objects are looked up in object databases
> changed.
> 
> Assuming there are two object databases configured (Main repo,
> and the quarantine directory, for example), the lookup order used
> to be:
> 
> 1. _quarantine dir_ packs
> 2. _main dir_ packs
> 3. _quarantine dir_ loose objects
> 4. _main dir_ loose objects
> 
> With Git 2.54, the order appears to have changed to:
> 
> 1. _quarantine dir_ packs
> 2. _quarantine dir_ loose objects
> 3. _main dir_ packs
> 4. _main dir_ loose objects
> 
> In my testing, within a well-packed repo, Git 2.50 actually never
> performed a loose object lookup.

Yeah, and that type of regression makes sense for what a593373b09 was
trying to do. But I think the v2.54 behavior is wrong. We should check
all packs before any loose objects.

I'm not sure of the correct fix. This is working against the whole "odb
sources are independent and abstract" refactoring that a593373b09 was
going for. But I think it's an important optimization. I guess the
abstract version would be that each source has "fast" and "slow" lookups
or something like that, and we check all fast ones before slow ones. But
that is pretty gross.

I'll leave it to Patrick to ponder further. I haven't really been paying
a lot of attention to the odb refactoring.

-Peff

^ permalink raw reply

* Performance regression in connectivity check during receive-pack (git 2.54)
From: Wolfgang Kritzinger @ 2026-07-21  3:17 UTC (permalink / raw)
  To: git

Hi!

I'm a developer working on the on-premise version of Bitbucket at
Atlassian.

We noticed pushes to Bitbucket got much slower after upgrading Git
on the server side from 2.50 to 2.54. I traced the slow part to
the connectivity check that receive-pack runs:

git rev-list --objects --stdin --not --exclude-hidden=receive --all \
--quiet --alternate-refs --progress=Checking connectivity

`strace` shows that after a push, 2.54 does a failing open() of
of numerous loose objects -- once in the quarantine (incoming)
directory and once in the main object store -- before finding it
in a pack:

openat(".../objects/tmp_objdir-incoming-XXXX/ed/58..", O_RDONLY) = ENOENT
openat(".../objects/ed/58..", O_RDONLY) = ENOENT

2.50 does not do this. In most customer deployments of Bitbucket,
the Git data lives on an NFS share. The extra latency on NFS makes
this process of checking for non-existent loose objects take too
long, the push essentially hangs at the "Checking connectivity" step.

I believe this new behavior was introduced in the recent object
database rework. After using bisect, I belive the problem can be
traced back to commit 8384cbcb4c.

I don't know the codebase well, but from what I can see is that
the order in which objects are looked up in object databases
changed.

Assuming there are two object databases configured (Main repo,
and the quarantine directory, for example), the lookup order used
to be:

1. _quarantine dir_ packs
2. _main dir_ packs
3. _quarantine dir_ loose objects
4. _main dir_ loose objects

With Git 2.54, the order appears to have changed to:

1. _quarantine dir_ packs
2. _quarantine dir_ loose objects
3. _main dir_ packs
4. _main dir_ loose objects

In my testing, within a well-packed repo, Git 2.50 actually never
performed a loose object lookup.

The current design seems to iterate over the configured object
databases and perform a pack and loose object lookup for each.

Is there a way to avoid these costly loose object lookups?

^ permalink raw reply

* What's cooking in git.git (Jul 2026, #09)
From: Junio C Hamano @ 2026-07-21  2:19 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.  They 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, it can be resubmitted when new interest
arises).

The fifth batch of topics have now graduated to the 'master' branch.

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/

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

* bc/rust-hash-cleanups (2026-07-18) 2 commits
 - rust: discard hash context when finished
 - hash: initialize context before cloning

 A few memory problems in the Rust interface to C hash functions have
 been corrected.  The 'Clone' implementation of 'CryptoHasher' now
 properly initializes the context before cloning, and its 'Drop'
 implementation now discards the context to prevent leaks.

 Will merge to 'next'.
 cf. <20260719080754.GA429688@coredump.intra.peff.net>
 source: <20260719010842.17991-1-sandals@crustytoothpaste.net>


* ja/doc-synopsis-style-yet-more (2026-07-19) 4 commits
 - doc: convert git-request-pull synopsis and options to new style
 - doc: convert git-send-email synopsis and options to new style
 - doc: convert git-format-patch synopsis and options to new style
 - doc: convert git-imap-send synopsis and options to new style

 Synopsis and options in the documentation for 'git format-patch',
 'git imap-send', 'git send-email', and 'git request-pull' have been
 updated to the modern style.

 Expecting a reroll.
 cf. <23179740.EfDdHjke4D@piment-oiseau>
 cf. <2418232.ElGaqSPkdT@piment-oiseau>
 source: <pull.2185.git.1784490878.gitgitgadget@gmail.com>


* hn/url-push-tracking (2026-07-20) 2 commits
 - remote: resolve URL-valued push tracking remotes
 - remote: pass repository to push tracking helper

 When the push remote is specified as a URL, the fetch refspec of a
 uniquely matching configured remote is now used to find and update
 the remote-tracking branch (e.g., '@{push}').

 Waiting for response.
 cf. <xmqq4ihtcx8g.fsf@gitster.g>
 cf. <xmqqfr1dcygh.fsf@gitster.g>
 source: <pull.2358.git.git.1784538618.gitgitgadget@gmail.com>

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

* bc/parse-options-exit-0-on-help (2026-07-07) 4 commits
  (merged to 'next' on 2026-07-10 at 775654e447)
 + parse-options: exit 0 on -h
 + rev-parse: have --parseopt callers exit 0 on --help
 + parse-options: add a separate case for help output on error
 + t1517: skip svn tests if svn is not installed

 Option parsing with 'git rev-parse --parseopt' and in most 'git'
 subcommands has been updated to exit with 0 (instead of 129) when the
 help option ('-h' or '--help') is requested directly by the user,
 aligning with standard Unix convention.

 Graduated to 'master'.
 cf. <20260708035930.GB41684@coredump.intra.peff.net>
 source: <20260708001557.3581080-1-sandals@crustytoothpaste.net>


* gr/t1410-reflog-exit-code (2026-07-08) 1 commit
  (merged to 'next' on 2026-07-10 at d0cf55ea54)
 + t1410-reflog.sh: avoid suppressing git's exit code in pipelines

 The pipelines in 't1410-reflog.sh' have been replaced with the
 'test_stdout_line_count' helper to avoid suppressing the exit code of
 'git' commands, ensuring failures are not hidden from the test suite.

 Graduated to 'master'.
 cf. <xmqqtsq8p18x.fsf@gitster.g>
 source: <20260709051229.40363-1-gatlavishweshwarreddy26@gmail.com>


* hf/unpack-trees-quadratic-scan (2026-07-08) 1 commit
  (merged to 'next' on 2026-07-12 at 744f1aede4)
 + unpack-trees: avoid quadratic index scan in next_cache_entry()

 The cache-scanning loop in 'next_cache_entry()' has been optimized
 to avoid rescanning already-unpacked index entries, preventing a
 quadratic performance slow-down when diffing the working tree
 against a commit with a pathspec matching early index entries.

 Graduated to 'master'.
 cf. <xmqqpl0xqh3n.fsf@gitster.g>
 source: <pull.2353.v2.git.git.1783546933992.gitgitgadget@gmail.com>


* jc/relnotes-2.55-rust-fix (2026-07-07) 1 commit
  (merged to 'next' on 2026-07-10 at 444d202a75)
 + Rust: fix description in Release Notes to 2.55

 A description in the release notes for Git 2.55.0 has been
 retroactively updated to clarify that Rust support is enabled by
 default, but still optional, and will become mandatory in Git 3.0.

 Graduated to 'master'.
 source: <xmqqpl0y4rpg.fsf@gitster.g>


* jc/submitting-patches-abandoning (2026-07-08) 1 commit
  (merged to 'next' on 2026-07-10 at 41b9b65b23)
 + SubmittingPatches: document how to retract a topic

 The 'SubmittingPatches' document has been updated to explicitly
 describe the expectation for contributors to retract or abandon their
 patch series when they are no longer pursuing it.

 Graduated to 'master'.
 cf. <ak6U07K1dQPlXxIp@nixos>
 source: <xmqqpl0xv25e.fsf@gitster.g>


* js/coverity-fixes-null-safety (2026-07-10) 12 commits
  (merged to 'next' on 2026-07-12 at 8d093f411d)
 + shallow: give write_one_shallow() its own hex buffer
 + shallow: fix NULL dereference
 + bisect: ensure non-NULL `head` before using it
 + pack-bitmap: handle missing bitmap for base MIDX
 + revision: avoid dereferencing NULL in `add_parents_only()`
 + replay: die when --onto does not peel to a commit
 + bisect: handle NULL commit in `bisect_successful()`
 + mailsplit: move NULL check before first use of file handle
 + reftable/stack: guard against NULL list_file in stack_destroy
 + remote: guard `remote_tracking()` against NULL remote
 + diff: handle NULL return from repo_get_commit_tree()
 + diffcore-break: guard against NULLed queue entries in merge loop

 Various code paths have been hardened against potential NULL-pointer
 dereferences and invalid file descriptor accesses flagged by
 Coverity.

 Graduated to 'master'.
 cf. <xmqqa4ryg84e.fsf@gitster.g>
 source: <pull.2174.v2.git.1783683577.gitgitgadget@gmail.com>


* kk/commit-graph-topo-levels-fix (2026-07-09) 2 commits
  (merged to 'next' on 2026-07-12 at 295a5f9b34)
 + commit-graph: propagate topo_levels slab to all chain layers
 + commit-graph: add trace2 instrumentation for generation DFS

 The 'topo_levels' slab was propagated only to the topmost layer of a
 split commit-graph chain, causing topological levels for commits in
 base layers to be recomputed during incremental writes.  This has been
 corrected.

 Graduated to 'master'.
 cf. <alFu8gZURKhYr1VE@com-79390>
 source: <pull.2170.v2.git.1783609382.gitgitgadget@gmail.com>


* kk/commit-reach-find-all-fix (2026-06-29) 2 commits
  (merged to 'next' on 2026-07-10 at 0444c74d81)
 + commit-reach: guard !FIND_ALL early exit with generation ordering check
 + t6600: add test for merge-base early exit with clock skew
 (this branch is used by kk/merge-base-exhaustion.)

 The early-exit optimization in 'paint_down_to_common()' has been
 gated on the queue being generation-ordered, fixing a bug where
 'git merge-base' (without '--all') could return incorrect results
 on repositories with v1 commit graphs and clock skew.

 Graduated to 'master'.
 cf. <xmqqjyr5v1gu.fsf@gitster.g>
 source: <pull.2162.git.1782739162.gitgitgadget@gmail.com>


* kk/reftable-tombstone-quadratic-fix (2026-07-10) 2 commits
  (merged to 'next' on 2026-07-12 at 4e60bb0027)
 + reftable: fix quadratic behavior in the presence of tombstones
 + t/perf: add perf test for ref tombstone scenarios

 The performance of ref updates and reads using the 'reftable' backend
 in the presence of many deletion tombstone records has been optimized
 by removing the tombstone suppression flag from the merged iterator
 and instead skipping tombstones at higher-level call sites where
 iteration bounds are known.

 Graduated to 'master'.
 cf. <alECc90WZ9RPqMaA@pks.im>
 source: <pull.2166.v3.git.1783679767.gitgitgadget@gmail.com>


* mm/test-grep-lint (2026-07-05) 6 commits
  (merged to 'next' on 2026-07-10 at 1916c07bf5)
 + t: add greplint to detect bare grep assertions
 + t: convert grep assertions to test_grep
 + t: fix Lexer line count for $() inside double-quoted strings
 + t: extract chainlint's parser into shared module
 + t: fix grep assertions missing file arguments
 + t/README: document test_grep helper

 The test suite has been updated to use the 'test_grep' helper instead
 of bare 'grep' for test assertions, allowing file contents to be
 printed on failure for easier debugging.  A new 'greplint' linter has
 been introduced to detect and prevent new bare 'grep' assertions from
 being added to the test suite.

 Graduated to 'master'.
 cf. <xmqqtsqedxmt.fsf@gitster.g>
 source: <pull.2135.v4.git.1783314119.gitgitgadget@gmail.com>


* ps/reftable-hardening (2026-07-03) 12 commits
  (merged to 'next' on 2026-07-10 at b8f4dd0ab9)
 + reftable/table: fix OOB read on truncated table
 + reftable/table: fix NULL pointer access when seeking to bogus offsets
 + reftable/block: fix OOB read with bogus restart offset
 + reftable/block: fix use of uninitialized memory when binsearch fails
 + reftable/block: fix OOB read with bogus restart count
 + reftable/block: fix OOB read with bogus block size
 + reftable/block: fix OOB write with bogus inflated log size
 + t/unit-tests: introduce test helper to write reftable blocks
 + reftable/record: don't abort when decoding invalid ref value type
 + reftable/basics: fix OOB read on binary search of empty range
 + oss-fuzz: add fuzzer for parsing reftables
 + meson: support building fuzzers with libFuzzer

 The 'reftable' code has been hardened against corrupted tables by
 fixing out-of-bounds writes, out-of-bounds reads, and abort calls
 during parsing.

 Graduated to 'master'.
 cf. <877bn5obz9.fsf@emacs.iotcl.com>
 source: <20260703-pks-reftable-hardening-v3-0-b87c555b9920@pks.im>


* ps/setup-split-discovery-and-setup (2026-07-07) 16 commits
  (merged to 'next' on 2026-07-10 at 1691a942ab)
 + setup: mark `set_git_work_tree()` as file-local
 + setup: pass worktree to `init_db()`
 + setup: drop redundant configuration of `startup_info->have_repository`
 + setup: make repository discovery self-contained
 + setup: propagate prefix via repository discovery
 + setup: drop static `cwd` variable
 + setup: move prefix into repository
 + setup: embed repository format in discovery
 + setup: introduce explicit repository discovery
 + setup: split up concerns of `setup_git_env_internal()`
 + setup: unify setup of shallow file
 + setup: mark bogus worktree in `apply_repository_format()`
 + setup: rename `check_repository_format_gently()`
 + Merge branch 'jk/repo-info-path-keys' into ps/setup-split-discovery-and-setup
 + Merge branch 'ps/setup-drop-global-state' into ps/setup-split-discovery-and-setup
 + Merge branch 'ps/refs-onbranch-fixes' into ps/setup-split-discovery-and-setup

 The repository discovery and repository configuration phases, which
 were previously intertwined in 'setup.c', have been split.  Repository
 discovery has been updated to populate a 'struct repo_discovery'
 without modifying the repository state, which is then taken by
 repository configuration to initialize the repository, paving the way
 for clean unification of repository configuration.

 Graduated to 'master'.
 cf. <87h5m9om0j.fsf@emacs.iotcl.com>
 source: <20260707-pks-setup-split-discovery-and-setup-v2-0-aab372cd227c@pks.im>


* sn/osxkeychain-rust-universal (2026-07-07) 3 commits
  (merged to 'next' on 2026-07-10 at fe82b5d188)
 + contrib: wire up osxkeychain in contrib/Makefile on macOS
 + Makefile: support universal macOS builds via RUST_TARGETS
 + Makefile: add $(RUST_LIB) prerequisite to osxkeychain

 The build system has been updated to support building universal macOS
 binaries when 'Rust' is enabled, by compiling separate static archives
 for each target triple listed in 'RUST_TARGETS' and combining them
 using the macOS 'lipo' tool.  The 'git-credential-osxkeychain' helper
 has been updated to link against '$(RUST_LIB)' when 'Rust' is enabled.

 Graduated to 'master'.
 cf. <xmqq4ii9teym.fsf@gitster.g>
 source: <pull.2288.v8.git.git.1783480879.gitgitgadget@gmail.com>


* tc/bundle-uri-empty-fix (2026-07-08) 2 commits
  (merged to 'next' on 2026-07-12 at 9da32fdaf7)
 + bundle-uri: stop sending invalid bundle configuration
 + bundle-uri: drain remaining response on invalid bundle-uri lines

 The client-side parser of the server-advertised bundle-URI list has
 been updated to drain the remaining response in order to avoid
 protocol desynchronization when the server sends a misconfigured list.
 Also, the server-side has been taught to omit empty configuration
 values instead of sending invalid key-value lines.

 Graduated to 'master'.
 cf. <xmqqtsq9qj5k.fsf@gitster.g>
 source: <20260708-toon-bundle-uri-no-uri-v2-0-09a03d8db556@iotcl.com>


* ty/migrate-ignorecase (2026-06-19) 2 commits
  (merged to 'next' on 2026-07-12 at 39e9fdb93f)
 + config: use repo_ignore_case() to access core.ignorecase
 + environment: move ignore_case into repo_config_values

 The global configuration variable 'ignore_case' (representing the
 'core.ignorecase' configuration) has been migrated into 'struct
 repo_config_values' to tie it to a specific repository instance.

 Graduated to 'master'.
 cf. <xmqqechaga7p.fsf@gitster.g>
 source: <20260619155152.642760-1-cat@malon.dev>


* wy/doc-myfirstcontribution-trim-quotes (2026-06-11) 1 commit
  (merged to 'next' on 2026-07-12 at adeaa999b6)
 + MyFirstContribution: mention trimming quoted text in replies

 The contributor guide has been updated to advise new contributors to
 trim irrelevant quoted text when replying to review comments, matching
 the existing advice given to reviewers.

 Graduated to 'master'.
 cf. <xmqqcxxwljue.fsf@gitster.g>
 source: <080402ff0ac8127b654dccea59a1bf643df62a5c.1781186476.git.wy@wyuan.org>

--------------------------------------------------
[Stalled]

* kh/doc-trailers (2026-06-10) 10 commits
 - doc: interpret-trailers: document comment line treatment
 - doc: interpret-trailers: commit to “trailer block” term
 - doc: interpret-trailers: join new-trailers again
 - 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 for 'git interpret-trailers' has been updated to explain
 the format of trailer keys (alphanumeric characters and hyphens),
 replace outdated terminology, define key terms upfront, and document
 how comment lines in the input are treated.

 Expecting a reroll for too long, stalled.
 cf. <729baf6b-53ea-4e8d-95ab-5935667e66c2@app.fastmail.com>
 source: <V3_CV_doc_int-tr_key_format.8a3@msgid.xyz>


* sn/rebase-update-refs-symrefs (2026-06-03) 1 commit
 - rebase: skip branch symref aliases

 'git rebase --update-refs' has been taught to resolve local branch
 symrefs to their referents before queuing updates, ensuring aliases of
 the current branch are skipped and duplicate updates are avoided to
 prevent failures when branch aliases are present.

 Waiting for response for too long, stalled.
 cf. <f982c386-e329-4ab0-b695-e540bcb9de3d@gmail.com>
 source: <pull.2126.v2.git.1780482436865.gitgitgadget@gmail.com>


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

 Configuration file locking has been updated to retry for a short
 period, avoiding failures when multiple processes attempt to update
 the configuration simultaneously.

 Waiting for response for too long, stalled.
 cf. <agrIrGwSMFlKTx9x@pks.im>
 source: <20260517132111.1014901-1-joerg@thalheim.io>

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

* tl/gitweb-shorten-hashes-with-modes (2026-07-17) 1 commit
 - gitweb: shorten index hashes with trailing file modes

 The object ID shortening and linking in the 'commitdiff' view of
 'gitweb' has been corrected to work even when the index line carries
 a trailing file mode.

 Needs review.
 source: <SA1PR10MB9977150C823C0751E53B150D5AF1C62@SA1PR10MB997715.namprd10.prod.outlook.com>


* kj/repo-info-more-path-keys (2026-07-17) 7 commits
 - repo: add path.git-prefix path key
 - repo: add path.grafts with absolute and relative suffix formatting
 - repo: add path.index with absolute and relative suffix formatting
 - repo: add path.hooks with absolute and relative suffix formatting
 - repo: add path.objects with absolute and relative suffix formatting
 - repo: add path.superproject-working-tree with absolute and relative suffixes
 - repo: add path.toplevel with absolute and relative suffix formatting

 The 'git repo info' command has been taught more keys to output
 paths of various repository components (such as the working tree
 root, superproject working tree, object database, etc.), supporting
 both absolute and relative path formats.

 Waiting for response.
 cf. <845D6852-98F5-4168-82CD-90B3B476BCF5@gmail.com>
 cf. <DB49CF15-4980-4213-8463-4C0FE2EC8438@gmail.com>
 source: <20260717133015.32040-1-jayatheerthkulkarni2005@gmail.com>


* sk/userdiff-swift (2026-07-17) 1 commit
 - userdiff: add support for Swift

 Userdiff patterns for Swift have been added, with support for
 Swift-specific constructs such as attributes, modifiers, failable
 initializers, and generics.

 Expecting a reroll.
 cf. <20260720095335.66241-1-diy2903@gmail.com>
 source: <20260717140232.6722-1-diy2903@gmail.com>


* ps/odb-move-loose-object-writing (2026-07-17) 10 commits
 - object-file: move logic to write loose objects
 - object-file: move `force_object_loose()`
 - object-file: force objects loose via generic interface
 - object-file: fix memory leak in `force_object_loose()`
 - odb: support setting mtime when writing objects
 - odb: lift object existence check out of the "loose" backend
 - odb: compute object hash in `odb_write_object_ext()`
 - t/u-odb-inmemory: implement wrapper for writing objects
 - odb: compute compat object ID in `odb_write_object_ext()`
 - Merge branch 'jt/receive-pack-use-odb-transactions' into HEAD
 (this branch uses jt/receive-pack-use-odb-transactions.)

 The logic to write loose objects has been refactored and moved from
 'object-file.c' to the loose backend source file 'odb/source-loose.c',
 making the loose backend more self-contained.  This is achieved by
 first refactoring 'force_object_loose()' to use generic ODB write
 interfaces instead of loose-backend internals.

 Needs review.
 source: <20260717-pks-odb-move-loose-object-writing-v1-0-46446a3cb5b7@pks.im>


* pw/rebase-fixup-fixes (2026-07-17) 2 commits
 - rebase: remember fixup -c after skipping fixup/squash
 - rebase -i: fix counting of fixups after rebase --skip

 Two bugs in how 'git rebase' handles skipped 'fixup' and 'squash'
 commands have been fixed.  One bug caused an incorrect commit count to
 be shown in the template message when multiple commands were skipped,
 and another caused the editor not to be opened when the final command
 in a chain containing 'fixup -c' was skipped.

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


* tc/last-modified-bloom (2026-07-17) 4 commits
 - last-modified: keep per-path Bloom filters for wildcard pathspecs
 - last-modified: check pathspec against Bloom filter first
 - revision: expose check for paths maybe changed in Bloom filter
 - revision: move bloom keyvec precondition into function

 The 'git last-modified' command has been optimized by using Bloom
 filters.  It now reuses revision walk filtering logic from 'git log'
 to pre-filter commits, and maintains per-path Bloom filters even when
 wildcard pathspecs are used.

 Expecting a reroll.
 cf. <87cxwl1lb4.fsf@emacs.iotcl.com>
 source: <20260717-toon-speed-up-last-modified-v1-0-410418f18614@iotcl.com>


* hn/bisect-reset-when-found (2026-07-20) 2 commits
 - bisect: add --reset-when-found to leave when done
 - bisect: let bisect_reset() optionally check out quietly

 The 'git bisect' command has been taught a
 '--reset-when-found[=<where>]' option that tells the command to
 automatically run 'git bisect reset' to jump back to the original
 state or to the found culprit.

 Will merge to 'next'?
 cf. <xmqqldb5d1d9.fsf@gitster.g>
 source: <pull.2335.v3.git.git.1784538619.gitgitgadget@gmail.com>


* js/coverity-unchecked-returns-fix (2026-07-14) 11 commits
 - bisect: handle dup() failure when redirecting stdout
 - bisect: check get_terms return at all call sites
 - bisect: check strbuf_getline_lf return when reading terms
 - transport-helper: warn when export-marks file cannot be finalized
 - transport-helper: check dup() return in get_exporter
 - compat/pread: check initial lseek for errors
 - last-modified: handle repo_parse_commit() failures
 - reftable tests: check reftable_table_init_ref_iterator() return
 - reftable/block: check deflateInit() return value
 - config: propagate launch_editor() failure in show_editor()
 - http: die on curl_easy_duphandle failure in get_active_slot

 A handful of code paths have been corrected to check return values
 from functions like 'curl_easy_duphandle()', 'deflateInit()',
 'lseek()', 'dup()', and 'strbuf_getline_lf()', resolving several
 Coverity warnings about unchecked returns.

 Waiting for response.
 cf. <xmqqldbdqciy.fsf@gitster.g>
 cf. <xmqqh5m1qcfh.fsf@gitster.g>
 cf. <alcvmX3b6y92KE4y@pks.im>
 cf. <alcvnm0xiOv5W0w_@pks.im>
 source: <pull.2179.git.1784069325.gitgitgadget@gmail.com>


* jk/diff-relative-cached-unmerged (2026-07-14) 1 commit
 - diff: ignore unmerged paths outside prefix with --relative --cached

 'git diff --relative' running with '--cached' has been corrected to
 avoid a segfault when encountering unmerged paths outside the
 prefix.

 Needs review.
 source: <20260715060523.GA517940@coredump.intra.peff.net>


* jc/submodule-helper-avoid-zu (2026-07-15) 1 commit
  (merged to 'next' on 2026-07-19 at b12d5d76f5)
 + submodule--helper: avoid use of %zu for now

 An accidental use of the '%zu' format specifier in 'git
 submodule--helper' has been corrected to use 'PRIuMAX' and cast the
 value to 'uintmax_t' to avoid portability issues.

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


* sk/t7614-do-not-hide-git-exit-status (2026-07-15) 1 commit
  (merged to 'next' on 2026-07-16 at 0d143986e7)
 + t7614: avoid hiding git's exit code in a pipe

 The test script 't/t7614-merge-signoff.sh' has been updated to avoid
 suppressing the exit code of 'git' commands in a pipe.

 Will merge to 'master'.
 cf. <xmqq1pd4m4ea.fsf@gitster.g>
 source: <20260715113344.3490-1-diy2903@gmail.com>


* ds/trace2-tolerate-failed-timestamp (2026-07-15) 1 commit
 - trace2: tolerate failed timestamp formatting

 The 'trace2' telemetry library has been updated to tolerate failures
 from system calls like 'gettimeofday()' and datetime formatting
 functions, replacing potential program crashes with blank placeholder
 timestamps in the traces.

 Waiting for response.
 cf. <xmqqzezlhgyo.fsf@gitster.g>
 cf. <al4yrXXoZiHLwSvE@com-79390>
 source: <pull.2178.git.1784131932489.gitgitgadget@gmail.com>


* mm/revision-pure-get-commit-action (2026-07-15) 1 commit
 - revision: make get_commit_action() a pure predicate

 The 'get_commit_action()' function has been refactored to be a pure
 predicate by moving the side-effecting line-level log range folding to
 'simplify_commit()'.  This ensures that evaluating a commit's action
 before the walk reaches it does not prematurely mutate its tracked
 line ranges, making it safer for potential lookahead evaluations.

 Needs review.
 source: <pull.2169.git.1784143793613.gitgitgadget@gmail.com>


* rs/remote-curl-simplify-push-specs (2026-07-14) 1 commit
  (merged to 'next' on 2026-07-19 at ff1b5528ba)
 + remote-curl: simplify passing of push specs

 The passing of push destination specifications in the 'remote-curl'
 helper has been simplified by removing the explicit 'count' parameter
 and relying on the NULL-termination of the array.

 Will merge to 'master'.
 source: <935883f3-3be4-4c51-9711-5208b9ef9ca1@web.de>


* kk/no-walk-pathspec-fix (2026-07-16) 2 commits
  (merged to 'next' on 2026-07-16 at 4dd6fb0e7e)
 + revision: fix --no-walk path filtering regression
 + Merge branch 'kk/streaming-walk-pqueue' into kk/no-walk-pathspec-fix

 The 'git rev-list --no-walk' command has been corrected to restore
 pathspec filtering, which was lost when the streaming walk was
 refactored.

 Will merge to 'master'.
 source: <pull.2181.git.1784198879711.gitgitgadget@gmail.com>


* cc/fast-import-usage (2026-07-16) 7 commits
 - fast-import: use struct option for usage string
 - fast-import: move command state globals into 'struct fast_import_state'
 - fast-import: introduce 'struct fast_import_state'
 - fast-import: localize 'i' into the 'for' loops using it
 - api-parse-options.adoc: document hidden and OPT_*_F option macros
 - api-parse-options.adoc: document per-option flags
 - parse-options: introduce OPT_HIDDEN_GROUP

 The usage string of 'git fast-import' has been updated to use the
 'parse_options' API for displaying help, and its SYNOPSIS in the
 documentation has been standardized to match.

 Waiting for response.
 cf. <xmqq4ihyehyb.fsf@gitster.g>
 source: <20260716165517.433849-1-christian.couder@gmail.com>


* ps/copy-wo-the-repository (2026-07-16) 1 commit
  (merged to 'next' on 2026-07-20 at 9e38da0efc)
 + copy: drop dependency on `the_repository`

 The 'copy_file()' and 'copy_file_with_time()' functions have been
 refactored to take a repository parameter, allowing the removal of the
 implicit dependency on the global 'the_repository' variable in
 'copy.c'.

 Will merge to 'master'.
 cf. <b0df688a-3b26-48f6-8b1c-98530483885e@gmail.com>
 cf. <xmqqo6g54k7m.fsf@gitster.g>
 source: <20260716-pks-copy-wo-the-repository-v2-1-8f5e32942929@pks.im>


* ps/refspec-wo-the-repository (2026-07-16) 3 commits
  (merged to 'next' on 2026-07-20 at 31044c3fc9)
 + refspec: stop depending on `the_repository`
 + refspec: let callers pass in hash algorithm when parsing items
 + refspec: group related structures and functions

 The dependency on the global 'the_repository' variable in the
 'refspec.c' API has been removed by passing the hash algorithm
 explicitly to refspec-parsing functions and storing it in 'struct
 refspec'.

 Will merge to 'master'.
 source: <20260716-pks-refspec-wo-the-repository-v1-0-aa40844d067f@pks.im>


* ps/writev (2026-07-16) 5 commits
 - fast-import: use writev(3p) to send cat-blob responses
 - sideband: use writev(3p) to send pktlines
 - wrapper: properly handle MAX_IO_SIZE in writev(3p)
 - wrapper: introduce writev(3p) wrappers
 - compat/posix: introduce writev(3p) wrapper

 A compatibility wrapper for 'writev(3p)' has been reintroduced,
 including fixes for CMake build and 'MAX_IO_SIZE' limits on NonStop.
 Calls to 'write(3p)' in 'send_sideband()' and 'cat_blob()' have been
 refactored to use 'writev(3p)' wrappers to reduce syscall overhead.

 Waiting for response.
 cf. <f8050598-392f-44c9-8d66-0454740a7a12@kdbg.org>
 cf. <a2676ec6-39d5-4220-8549-10a17daec668@hogyros.de>
 cf. <xmqqfr1ig0hv.fsf@gitster.g>
 source: <20260716-pks-reintroduce-writev-v1-0-ea9038c884bc@pks.im>


* sc/wt-status-avoid-quadratic-insertion (2026-07-18) 1 commit
  (merged to 'next' on 2026-07-20 at 9330d42a4a)
 + wt-status: avoid repeated insertion for untracked paths

 The enumeration of untracked and ignored files in 'git status' has
 been optimized by avoiding quadratic complexity insertion into string
 lists, reducing the construction cost from O(n^2) to O(n log n).

 Will merge to 'master'.
 cf. <20260718083828.GE22588@coredump.intra.peff.net>
 source: <20260718081449.26747-1-sahityajb@gmail.com>


* tb/send-pack-no-ref-delta (2026-07-12) 4 commits
 - send-pack: honor `no-ref-delta` capability
 - pack-objects: support reuse with `--no-ref-delta`
 - pack-objects: introduce `--no-ref-delta`
 - t/helper: teach pack-deltas to list delta entries

 'git send-pack' has been taught to refrain from sending 'REF_DELTA'
 encoded packfiles when the other side asks it to.

 Needs review.
 source: <alQ7WKITYDXfiVn9@com-79390>


* cc/doc-fast-export-synopsis-fix (2026-07-13) 1 commit
  (merged to 'next' on 2026-07-16 at b1dbc0cb3f)
 + fast-export: standardize usage string and SYNOPSIS

 The usage string and SYNOPSIS for 'git fast-export' have been
 standardized to make them consistent with each other and with other
 commands.

 Will merge to 'master'.
 cf. <alX5Nl8uX4ctVqo3@pks.im>
 cf. <xmqq4ii228dd.fsf@gitster.g>
 source: <20260713124153.245268-1-christian.couder@gmail.com>


* sk/t1100-modernize (2026-07-14) 2 commits
  (merged to 'next' on 2026-07-16 at 621ca4ca5f)
 + t1100: move creation of expected output into setup test
 + t1100: modernize test style

 The test script 't/t1100-commit-tree-options.sh' has been modernized
 by converting test cases to the modern style (using single quotes and
 tab indentation) and moving the creation of the expected file inside
 the setup test so it runs under the protection of the test harness.

 Will merge to 'master'.
 cf. <xmqq4ii1v7x0.fsf@gitster.g>
 source: <20260714122033.61947-1-diy2903@gmail.com>


* tn/packfile-uri-concurrency (2026-07-13) 2 commits
 - fetch-pack: accept "pack" output for packfile URIs
 - http: use unique tempfiles for packfile URI downloads

 Concurrent downloads of packfiles via packfile URIs have been
 supported by using unique temporary files, preventing corruption when
 multiple processes fetch the same pack.  The 'fetch-pack' command has
 also been updated to tolerate pre-existing '.keep' files.

 Expecting a reroll.
 cf. <alaAi4vNwi-KabYV@com-76773>
 source: <alVn-QmK3K91_tkH@com-76773>


* rs/strbuf-avoid-redundant-reset (2026-07-14) 1 commit
  (merged to 'next' on 2026-07-16 at f258ce38ba)
 + strbuf: avoid redundant reset in strbuf_getwholeline()

 A redundant 'strbuf_reset()' call in the 'HAVE_GETDELIM' path of
 'strbuf_getwholeline()' has been removed, as 'getdelim()' overwrites
 the buffer and the length is updated afterward.

 Will merge to 'master'.
 cf. <xmqq8q7dv82b.fsf@gitster.g>
 cf. <20260714214941.GB4095533@coredump.intra.peff.net>
 source: <d4ffe7fb-f782-4f06-9e3b-f72729d1e225@web.de>


* rs/tempfile-wo-the-repository (2026-07-14) 5 commits
 - use repo_hold_lock_file_for_update{,_mode,_timeout}() with custom repos
 - tempfile: stop using the_repository
 - lockfile: add repo_hold_lock_file_for_update{,_timeout}{,_mode}()
 - refs/packed: use repo_create_tempfile()
 - tempfile: add repo_create_tempfile{,_mode}()

 The tempfile and lockfile APIs have been refactored to stop depending
 on the 'the_repository' global variable, and their callers have been
 updated to use the repository-aware variants.

 Will merge to 'next'?
 cf. <aldYW4TPUqgDMRcf@pks.im>
 cf. <3c0a8031-7082-422a-b474-938418682b60@web.de>
 cf. <xmqqmrvmn6a5.fsf@gitster.g>
 source: <20260714175956.54601-1-l.s.r@web.de>


* js/pack-objects-delta-size-t (2026-07-09) 12 commits
 - git-zlib: widen `git_deflate_bound()` to `size_t`
 - t/helper/test-pack-deltas: widen `do_compress()`'s maxsize local to `size_t`
 - http-push: widen `start_put()`'s size local from `ssize_t` to `size_t`
 - diff: widen `deflate_it()`'s bound local from int to `size_t`
 - archive-zip: widen `zlib_deflate_raw()`'s maxsize local to `size_t`
 - packfile, git-zlib: widen `use_pack()` and zstream avail fields to `size_t`
 - delta: widen `create_delta()` and `diff_delta()` to `size_t`
 - pack-objects: widen `mem_usage` and `try_delta()`'s out-param to `size_t`
 - pack-objects: widen `free_unpacked()` return to `size_t`
 - pack-objects: widen delta-cache accounting to `size_t`
 - delta: widen `create_delta_index()` parameter to `size_t`
 - diff-delta: widen `struct delta_index`' size fields to `size_t`

 The 'pack-objects' and delta-encoding code paths have been updated to
 use 'size_t' instead of 'unsigned long' for object sizes and offset
 limits, avoiding potential truncation issues on 64-bit Windows.

 Needs review.
 source: <pull.2175.git.1783615780.gitgitgadget@gmail.com>


* cl/b4-cover-change-id (2026-07-10) 1 commit
  (merged to 'next' on 2026-07-13 at 15c7ad9a3f)
 + b4: include change-id in cover template

 The in-tree 'b4' cover letter template has been updated to include the
 'change-id' trailer, ensuring that sent tags generated by 'b4' contain
 the required tracking information for subsequent runs.

 Will merge to 'master'.
 source: <20260710-add-change-id-to-b4-template-v1-1-1bd37a25064e@black-desk.cn>


* ps/odb-stream-double-close-fix (2026-07-10) 1 commit
  (merged to 'next' on 2026-07-13 at dd2c5795b7)
 + object-file: fix closing object stream twice

 The stream-based object signature verification path has been
 corrected to avoid double-closing the stream on read errors.

 Will merge to 'master'.
 source: <20260710-pks-odb-stream-double-close-v1-1-d5fa233a37c7@pks.im>


* pz/fetch-submodule-errors-config (2026-07-16) 2 commits
 - fetch: add fetch.submoduleErrors to make submodule fetch errors non-fatal
 - submodule: fix premature failure in recursive submodule fetch

 The 'git fetch' command has been updated to allow configuring how
 submodule fetch errors are handled.  A new configuration variable
 'fetch.submoduleErrors' and a corresponding '--submodule-errors'
 command-line option have been introduced, allowing users to make
 submodule fetch errors non-fatal (warn instead of fail).
 Additionally, a premature failure during recursive submodule fetches
 has been fixed by deferring the error until the OID-based retry phase
 also fails.

 Needs review.
 source: <20260716140956.1023740-1-paulius.zaleckas@gmail.com>


* gr/add-e-use-apply-api (2026-07-10) 1 commit
 - builtin/add.c: replace run_command() with direct apply_all_patches() call

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

 Needs review.
 source: <20260711061246.58079-1-gatlavishweshwarreddy26@gmail.com>


* fz/rebase-autosquash-empty (2026-07-11) 1 commit
 . sequencer: honor --empty when a fixup!/squash! empties its target

 A commit that is emptied by melding a 'fixup!' or 'squash!' commit
 during 'git rebase --autosquash' is now handled according to the
 '--empty' option, allowing it to be dropped, kept, or to halt the
 rebase.

 Ejected due to conflicts with 'pw/rebase-drop-notes-with-commit'.

 Waiting for response.
 cf. <690b965e-5f07-4aa4-a64c-96e60a86d73b@gmail.com>
 source: <20260711-fz-autosquash-empty-v3-1-d227b63eb511@gmail.com>


* dm/submodule-update-i-shorthand (2026-07-07) 1 commit
  (merged to 'next' on 2026-07-15 at 55ef0fb748)
 + submodule--helper: accept '-i' shorthand for update --init

 The '-i' shorthand for the '--init' option, which was accepted by the
 'git submodule update' command until it was broken in a modernization
 of the option-parsing code, has been restored.

 Will merge to 'master'.
 cf. <xmqq8q7ltf51.fsf@gitster.g>
 source: <20260708-submodule-init-v1-1-719456077262@atmark-techno.com>


* mm/lib-httpd-cgi-safe (2026-07-10) 3 commits
 - t/README: document writing concurrency-safe helpers
 - t/lib-httpd: make http-429 first-request check atomic
 - t/lib-httpd: fix apply-one-time-script race under concurrent requests

 CGI helper scripts used by HTTP-related test scripts have been updated
 to use atomic filesystem operations, preventing race conditions when
 Apache handles concurrent requests.

 Needs review.
 source: <pull.2171.v2.git.1783704657.gitgitgadget@gmail.com>


* ps/odb-pluggable-housekeeping (2026-07-12) 12 commits
 - odb: make optimizations pluggable
 - builtin/gc: fix signedness issues in ODB-related functionality
 - builtin/gc: refactor ODB optimizations to operate on "files" source
 - builtin/gc: introduce `odb_optimize_required()`
 - builtin/gc: move geometric repacking into `odb_optimize()`
 - builtin/gc: introduce object database optimization options
 - builtin/gc: inline config values specific to the "files" backend
 - builtin/gc: make repack arguments self-contained
 - builtin/gc: extract object database optimizations into separate function
 - builtin/gc: move worktree and rerere tasks before object optimizations
 - odb: run "pre-auto-gc" hook for all maintenance tasks
 - t7900: simplify how we check for maintenance tasks

 Object database housekeeping in 'git gc' and 'git maintenance' has
 been refactored to be pluggable.  The files-backend specific logic,
 including incremental and geometric repacking as well as object
 pruning, has been moved out of the command implementation and into the
 files object database source, enabling future alternative object
 database backends to implement their own housekeeping services.

 Waiting for response.
 cf. <xmqqwluyyhv1.fsf@gitster.g>
 source: <20260713-b4-pks-odb-optimize-v2-0-9c2c3ee94b38@pks.im>


* ps/odb-for-each-object-filter (2026-07-14) 10 commits
  (merged to 'next' on 2026-07-16 at 8f30e80d33)
 + builtin/cat-file: filter objects via object database
 + odb: introduce object filters to `odb_for_each_object()`
 + pack-bitmap: introduce function to open bitmap for a single source
 + pack-bitmap: drop `_1` suffix from functions that open bitmaps
 + pack-bitmap: iterate object sources when opening bitmaps
 + pack-bitmap: allow aborting iteration of bitmapped objects
 + pack-objects: drop unused return value from add_object_entry()
 + pack-bitmap: mark object filter as `const`
 + odb/source-packed: improve lookup when enumerating objects
 + Merge branch 'ps/odb-drop-whence' into ps/odb-for-each-object-filter

 The object database enumeration interface 'odb_for_each_object()'
 has been taught to accept object filters, allowing the underlying
 backends to optimize the traversal by using reachability bitmaps
 when available.  'git cat-file --batch-all-objects' has been updated
 to use this generic interface, simplifying its code and avoiding
 direct access to ODB backend internals.

 Will merge to 'master'.
 cf. <874ii0h2uf.fsf@emacs.iotcl.com>
 source: <20260715-pks-odb-for-each-object-filter-v4-0-616d7adf7fb7@pks.im>


* ps/refs-wo-the-repository (2026-07-15) 7 commits
  (merged to 'next' on 2026-07-19 at 12685f410c)
 + refs: remove remaining uses of `the_repository`
 + worktree: pass repository to public functions
 + worktree: pass repository to file-local functions
 + worktree: refactor code to use available repositories
 + refs/files: drop `USE_THE_REPOSITORY_VARIABLE`
 + refs/packed: de-globalize handling of "core.packedRefsTimeout"
 + Merge branch 'ps/refs-writing-subcommands' into ps/refs-wo-the-repository

 The ref subsystem and the worktree API have been refactored to pass a
 repository pointer down the call chain, allowing them to drop
 references to the global 'the_repository' variable.  As part of this,
 the handling of the 'core.packedRefsTimeout' configuration has been
 moved into the per-repository ref store structure.

 Will merge to 'master'.
 source: <20260716-pks-refs-wo-the-repository-v3-0-db0a804e0224@pks.im>


* ds/sparse-index-ita-crash (2026-07-06) 1 commit
 - sparse-index: avoid crash on intent-to-add entry outside the cone

 A crash in the 'sparse-index' collapse code when encountering an
 invalidated cache-tree node (due to an intent-to-add path) has been
 fixed by avoiding collapsing such subtrees.

 Needs review.
 source: <pull.2167.git.1783345853272.gitgitgadget@gmail.com>


* ij/subtree-reject-v2-config (2026-07-06) 2 commits
 - git-subtree: Bail out if we find output from Rust rewrite (test)
 - git-subtree: Bail out if we find output from Rust rewrite

 The shell script implementation of 'git subtree' has been updated to
 check for the presence of the configuration file of the new Rust
 implementation, preventing users from accidentally running the old
 script on repositories already managed by the new tool.

 Expecting a reroll.
 cf. <27219.20156.438730.881821@chiark.greenend.org.uk>
 source: <20260706115816.20267-1-ijackson@chiark.greenend.org.uk>


* jm/t0213-skip-emulated-ancestry-tests (2026-07-06) 1 commit
 - t0213: skip ancestry tests under user-mode emulation

 The 'TRACE2_ANCESTRY' prerequisite in the 't0213' test script has been
 refined to avoid failures under user-mode emulation, by verifying that
 the ancestry collector reports the expected process names rather than
 the emulator binary name.

 Will merge to 'next'.
 cf. <xmqqa4s38rbe.fsf@gitster.g>
 source: <pull.2168.git.1783359242130.gitgitgadget@gmail.com>


* zy/apply-abandoned-header-fix (2026-07-01) 1 commit
 - apply: avoid leaking abandoned git-header state

 A candidate 'git diff' header parsed by 'git apply' has been isolated
 in a temporary structure, preventing any partially parsed state from
 polluting the main patch structure and causing assertions to trip if
 the header is ultimately rejected.

 Needs review.
 source: <20260702041759.51572-1-zhihao.yao@njit.edu>


* ml/t9811-replace-test-f (2026-07-11) 2 commits
  (merged to 'next' on 2026-07-15 at ffb7fcad15)
 + t9811: replace 'test -f' and '! test -f' with 'test_path_*'
 + t9811: break long && chains into multiple lines

 The test script 't/t9811-git-p4-label-import.sh' has been
 modernized to use 'test_path_is_file' and 'test_path_is_missing'
 instead of raw 'test -f' and '! test -f' calls.

 Will merge to 'master'.
 cf. <alTHrUEh4_O5ROeu@pks.im>
 source: <20260711160447.99708-1-marcelomlage@usp.br>


* cl/conditional-config-on-worktree-path (2026-07-09) 2 commits
  (merged to 'next' on 2026-07-15 at 86ca33c437)
 + 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 been taught to use the location of the
 worktree in its condition.

 Will merge to 'master'.
 cf. <alTJCTKR9jOWfgbk@pks.im>
 source: <20260710-includeif-worktree-v8-0-04686d8a616c@black-desk.cn>


* bl/t7412-use-test-path-helpers (2026-06-29) 1 commit
 - submodule absorbgitdirs tests: use test_* helper functions

 The test script 't7412' that tests 'git submodule absorbgitdirs' has
 been modernized to use 'test_path_is_file', 'test_path_is_dir', and
 'test_path_is_missing' helper functions instead of raw 'test -[fde]'
 commands.

 Waiting for response.
 cf. <akTKHfKPsP3-Rn31@pks.im>
 source: <20260630020220.1559190-1-bblima@usp.br>


* pw/rebase-drop-notes-with-commit (2026-07-15) 9 commits
  (merged to 'next' on 2026-07-20 at 5475c9f935)
 + sequencer: do not record dropped commits as rewritten
 + sequencer: use an enum to represent result of picking a commit
 + sequencer: simplify pick_one_commit()
 + sequencer: remove unnecessary condition in pick_one_commit()
 + sequencer: simplify handling of fixup with conflicts
 + sequencer: remove unnecessary "or" in pick_one_commit()
 + sequencer: never reschedule on failed commit
 + sequencer: be more careful with external merge
 + t3400: restore coverage for note copying with apply backend

 The rebase post-rewrite notes-copying logic has been corrected.  When
 a commit is dropped during rebase (e.g., because its changes are
 already upstream), it is no longer recorded as rewritten, preventing
 its notes from being copied to an unrelated commit.

 Will merge to 'master'.
 cf. <xmqqy0f5d25g.fsf@gitster.g>
 source: <cover.1784128921.git.phillip.wood@dunelm.org.uk>


* tb/repack-geometric-cruft (2026-06-28) 11 commits
 - SQUASH??? bare grep !???
 - repack: support combining '--geometric' with '--cruft'
 - pack-objects: support '--refs-snapshot' with 'follow-reachable'
 - pack-objects: introduce '--stdin-packs=follow-reachable'
 - pack-objects: extract `stdin_packs_add_all_pack_entries()`
 - repack-geometry: drop unused redundant-pack removal
 - repack: delete geometric packs via existing_packs
 - repack: teach MIDX retention about geometric rollups
 - repack: mark geometric progression of packs as retained
 - repack: extract `locate_existing_pack()` helper
 - repack: unconditionally exclude non-kept packs

 'git repack' has been taught to accept '--geometric' and '--cruft'
 together.  When both are given, non-cruft packs are rolled up by the
 geometric repack as usual, while a separate cruft pack is written to
 collect unreachable objects.

 Waiting for response.
 cf. <xmqq8q8068f7.fsf@gitster.g>
 cf. <xmqqpl1d56dd.fsf@gitster.g>
 source: <cover.1782500507.git.me@ttaylorr.com>


* jt/receive-pack-use-odb-transactions (2026-07-10) 11 commits
  (merged to 'next' on 2026-07-15 at aba57e3365)
 + builtin/receive-pack: stage incoming objects via ODB transactions
 + builtin/receive-pack: drop redundant tmpdir env
 + odb/transaction: introduce ODB transaction flags
 + odb/transaction: add transaction env interface
 + odb/transaction: propagate commit errors
 + odb/transaction: propagate begin errors
 + object-file: propagate files transaction errors
 + object-file: drop check for inflight transactions
 + object-file: embed transaction flush logic in commit function
 + object-file: rename files transaction fsync function
 + object-file: rename files transaction prepare function
 (this branch is used by ps/odb-move-loose-object-writing.)

 'git receive-pack' has been refactored to use ODB transaction
 interfaces instead of directly managing 'tmp_objdir' for staging
 incoming objects, bringing it closer to being ODB backend agnostic.

 Will merge to 'master'.
 cf. <alR1P-RGZNmjyiUE@pks.im>
 source: <20260710163722.2962278-1-jltobler@gmail.com>


* ty/migrate-excludes-file (2026-07-13) 10 commits
 - repository: adjust the comment of config_values_private_
 - environment: move object_creation_mode into repo_config_values
 - environment: move autorebase into repo_config_values
 - environment: move push_default into repo_config_values
 - environment: migrate apply_default_whitespace and apply_default_ignorewhitespace
 - environment: move askpass_program into repo_config_values
 - environment: move pager_program into repo_config_values
 - environment: move editor_program into repo_config_values
 - environment: move excludes_file into repo_config_values
 - repository: introduce repo_config_values_clear()

 The 'excludes_file' and various other global configuration variables
 (including 'editor_program', 'pager_program', 'askpass_program', and
 'push_default') have been migrated into the per-repository structure.

 Needs review.
 cf. <xmqq8q7961xe.fsf@gitster.g>
 source: <20260714032525.1611141-1-cat@malon.dev>


* ps/libgit-in-subdir (2026-07-12) 3 commits
 . Move libgit.a sources into separate "lib/" directory
 . t/helper: prepare "test-example-tap.c" for introduction of "lib/"
 . Merge branch 'ps/odb-source-packed' into ps/libgit-in-subdir

 The source files for 'libgit.a' have been moved into a new 'lib/'
 directory to clean up the top-level directory and clearly separate
 library code.

 Ejected for now, as it causes too many evil merges with other topics.

 Needs review.
 cf. <alR9GDNTbdjWB4dq@szeder.dev>
 cf. <2d455ecf-972e-e3ce-54bc-683050c04282@gmx.de>
 source: <20260713-pks-libgit-in-subdir-v4-0-696240876eb1@pks.im>


* mm/line-log-limited-ops (2026-06-27) 7 commits
 - diffcore-pickaxe: scope -G to the -L tracked range
 - diff: support --check with -L line ranges
 - line-log: support diff stat formats with -L
 - diff: extract a line-range diff helper for reuse
 - diff: emit -L hunk headers via xdiff's formatter
 - diff: simplify the line-range filter by classifying removals immediately
 - diff: rename and group the line-range filter for clarity

 The 'git log -L<range>:<path>' command has been taught to limit
 various 'diff' operations, such as '--stat', '--check', and '-G', to
 the specified range and path.

 Needs review.
 source: <pull.2152.v2.git.1782581342.gitgitgadget@gmail.com>


* hn/history-squash (2026-07-20) 5 commits
 - history: re-edit a squash with every message
 - sequencer: share the squash message marker helpers and flags
 - history: add squash subcommand to fold a range
 - history: give commit_tree_ext a message template
 - history: extract helper for a commit's parent tree

 The experimental 'git history' command has been taught a new 'squash'
 subcommand to fold a range of commits into a single commit, with any
 descendants replayed on top.

 Needs review.
 source: <pull.2337.v10.git.git.1784536024.gitgitgadget@gmail.com>


* tb/midx-incremental-custom-base (2026-06-12) 3 commits
 - midx-write: include packs above custom incremental base
 - midx: pass custom '--base' through incremental writes
 - t5334: expose shared `nth_line()` helper

 The 'git multi-pack-index write --incremental' command has been
 corrected to properly honor the '--base' option.  Previously, the
 custom base was ignored by the normal write path; packs from layers
 above the selected base were incorrectly skipped by the pack exclusion
 logic, and reachability closure for bitmaps was broken.

 Needs review.
 source: <cover.1781294771.git.me@ttaylorr.com>


* td/ref-filter-memoize-contains (2026-06-12) 3 commits
  (merged to 'next' on 2026-07-19 at 5b640e33a1)
 + commit-reach: die on contains walk errors
 + ref-filter: memoize --contains with generations
 + commit-reach: reject cycles in contains walk

 'git branch --contains' and 'git for-each-ref --contains' have been
 optimized to use the memoized commit traversal previously used only by
 'git tag --contains', significantly speeding up connectivity checks
 across many candidate refs with shared history.

 Will merge to 'master'.
 cf. <20260716091924.GB1212956@coredump.intra.peff.net>
 source: <20260612-ref-filter-memoized-contains-v4-0-5ed39fd001dd@gmail.com>


* tc/replay-linearize (2026-07-07) 3 commits
  (merged to 'next' on 2026-07-09 at 371c2e9c3b)
 + replay: offer an option to linearize the commit topology
 + replay: resolve the replay base outside pick_regular_commit()
 + replay: add helper to put entry into replayed_commits

 The 'git replay' command has been taught the '--linearize' option to
 drop merge commits and linearize the replayed history, mimicking 'git
 rebase --no-rebase-merges'.

 On hold, waiting for response from the author.
 cf. <xmqq5x2qz42z.fsf@gitster.g>
 cf. <CABPp-BGzU9KHGF1nipi2HZaa1AiikMKGGaapQzHVH06wO4V1ww@mail.gmail.com>
 source: <20260707-toon-git-replay-drop-merges-v7-0-808ab9b4afa6@iotcl.com>


* ps/cat-file-remote-object-info (2026-07-18) 13 commits
 - cat-file: make remote-object-info allow-list adapt to the server
 - cat-file: add remote-object-info to batch-command
 - transport: add client support for object-info
 - serve: advertise object-info feature
 - protocol-caps: check object existence regardless of the attributes requested
 - fetch-pack: move fetch initialization
 - connect: make write_fetch_command_and_capabilities() more generic
 - fetch-pack: move write_fetch_command_and_capabilities() to connect.c
 - fetch-pack: use unsigned int for hash_algo variable
 - fetch-pack: drop the static advertise_sid variable
 - t1006: extract helper functions into new 'lib-cat-file.sh'
 - cat-file: declare loop counter inside for()
 - transport-helper: fix memory leak of helper on disconnect

 The 'remote-object-info' command has been added to 'git cat-file
 --batch-command', allowing clients to request object metadata
 (currently size) from a remote server via protocol v2 without
 downloading the entire object.  Format placeholders are dynamically
 filtered on the client based on server-advertised capabilities,
 returning empty strings for inapplicable or unsupported fields.

 Needs review.
 source: <20260718-ps-eric-work-rebase-v20-0-0c13962ac532@gmail.com>


* mm/diff-process-hunks (2026-07-15) 9 commits
 . line-log: consult diff process for range tracking
 . diff: consult diff process for --stat counts
 . blame: consult diff process for no-hunk detection
 . diff: bypass diff process with --no-ext-diff and in format-patch
 . diff: add long-running diff process via diff.<driver>.process
 . sub-process: separate process lifecycle from hashmap management
 . userdiff: add diff.<driver>.process config
 . xdiff: support external hunks via xpparam_t
 . gitattributes: document how external diff drivers relate to diff features

 A new 'diff.<driver>.process' configuration has been introduced to
 allow a long-running external process to act as a hunk provider,
 enabling external tools to control which lines Git considers changed
 while leaving all output formatting (word diff, color, blame, etc.) to
 Git's standard pipeline.

 Ejected for now, as it conflicts badly with 'mm/line-log-limited-ops'.

 Expecting a reroll.
 cf. <xmqq8q7aj3b0.fsf@gitster.g>
 cf. <CAC2QwmKRp90hmBAckug9PPvvD53Pi53q5csZhi15LRhzdQasQg@mail.gmail.com>
 source: <pull.2120.v5.git.1784149323.gitgitgadget@gmail.com>


* ty/migrate-trust-executable-bit (2026-07-20) 4 commits
 - environment: move has_symlinks into repo_config_values
 - environment: move trust_executable_bit into repo_config_values
 - read-cache: pass 'repo' to 'ce_mode_from_stat()'
 - read-cache: remove redundant extern declarations

 The 'trust_executable_bit' (coming from the 'core.filemode'
 configuration) has been migrated into 'struct repo_config_values' to
 tie it to a specific repository instance.

 Needs review.
 source: <20260720105335.3202013-1-cat@malon.dev>


* za/completion-hide-dotfiles (2026-06-20) 2 commits
 - completion: hide dotfiles by default for path completion
 - completion: hide dotfiles for selected path completion

 Path completion for commands like 'git rm' and 'git mv' has been
 updated to hide dotfiles by default unless the user explicitly starts
 the path with a dot, matching standard shell-completion behavior.

 Waiting for response, stalled.
 cf. <xmqqik71t3nr.fsf@gitster.g>
 source: <pull.2311.v3.git.git.1781978156.gitgitgadget@gmail.com>


* ec/commit-fixup-options (2026-05-26) 2 commits
 - commit: allow -c/-C for all kinds of --fixup
 - commit: allow -m/-F for all kinds of --fixup

 Support for '-m', '-F', '-c', or '-C' options to supply a commit log
 message from outside the editor has been added for all 'git commit
 --fixup' variations.

 Needs review.
 source: <cover.1779792311.git.erik@cervined.in>


* kh/doc-replay-config (2026-06-05) 4 commits
 - doc: replay: move “default” to the right-hand side
 - doc: replay: use a nested description list
 - doc: replay: improve config description
 - doc: link to config for git-replay(1)

 Documentation for 'git replay' has been updated to refer to its
 configuration variables.

 Waiting for response for too long, stalled.
 cf. <87cxwxofgv.fsf@emacs.iotcl.com>
 source: <V3_CV_doc_replay_config.780@msgid.xyz>


* hn/branch-delete-merged (2026-07-14) 7 commits
 - branch: add --dry-run for --delete-merged
 - branch: add branch.<name>.deleteMerged opt-out
 - branch: add --delete-merged <branch>
 - branch: prepare delete_branches for a bulk caller
 - branch: let delete_branches skip unmerged branches on bulk refusal
 - branch: convert delete_branches() to a flags argument
 - branch: add --forked filter for --list mode

 The 'git branch' command has been taught the '--delete-merged' option
 to remove local branches that are already merged into their tracked
 remote-tracking branches.

 Expecting a reroll.
 cf. <xmqqtspvptqc.fsf@gitster.g>
 cf. <CAHwyqnXdaPeO12+p=_+_ttrknV0-VqTMnH-suS66yZ4stsBKnQ@mail.gmail.com>
 source: <pull.2285.v19.git.git.1784053493.gitgitgadget@gmail.com>


* hn/checkout-track-fetch (2026-06-24) 2 commits
 - checkout: extend --track with a "fetch" mode to refresh start-point
 - branch: expose helpers for finding the remote owning a tracking ref

 The 'git checkout --track=...' command has been taught to optionally
 fetch the branch from the remote that the new branch will work with.

 Waiting for response for too long, stalled.
 cf. <xmqq5x37h6fj.fsf@gitster.g>
 cf. <CAL71e4MiijEiM26TKJcOYT7L4pfQeMM_F2oT3U3igP-wOZm2Ag@mail.gmail.com>
 source: <pull.2281.v15.git.git.1782338098.gitgitgadget@gmail.com>


* ps/shift-root-in-graph (2026-07-14) 7 commits
  (merged to 'next' on 2026-07-19 at bebf13a239)
 + graph: add --[no-]graph-indent and log.graphIndent
 + graph: move config reading into graph_read_config()
 + graph: wrap cascading commits after 4 columns
 + graph: indent visual root in graph
 + graph: add a 2 commit buffer for lookahead
 + revision: add next_commit_to_show()
 + lib-log-graph: move check_graph function

 'git log --graph' has been modified to visually distinguish parentless
 'root' commits (and commits that become roots due to history
 simplification) by indenting them, preventing them from appearing
 falsely related to unrelated commits rendered immediately above them.

 Will merge to 'master'.
 cf. <CA+J6zkQNzEAhhY74qDrOwfFVrshEF7YFxWRRkwE3ttJo15ZbAg@mail.gmail.com>
 source: <20260714-ps-pre-commit-indent-v12-0-d50938e006df@gmail.com>


* kk/merge-base-exhaustion (2026-07-11) 11 commits
 - commit-reach: remove commit-date ordering fallback
 - commit-reach: move min_generation check into paint_queue_get()
 - commit-reach: terminate merge-base walk when one paint side is exhausted
 - commit-reach: introduce struct paint_state with per-side counters
 - t6600: add clock-skew topologies and step counts for edge cases
 - commit-reach: add trace2 instrumentation to paint_down_to_common()
 - t6099, t6600: add side-exhaustion regression tests
 - t6600: add test cases for side-exhaustion edge cases
 - test-lib-functions: improve diagnostic output for trace2 data assertions
 - Documentation/technical: add paint-down-to-common doc
 - Merge branch 'kk/commit-reach-find-all-fix' into kk/merge-base-exhaustion

 The merge-base computation has been optimized by stopping the walk
 early when one side's exclusive commits in the queue are exhausted,
 yielding significant speedups for queries with one-sided histories.

 Needs review.
 cf. <xmqqse5en8wz.fsf@gitster.g>
 source: <pull.2149.v6.git.1783776466.gitgitgadget@gmail.com>

^ permalink raw reply

* Re: [GSoC Patch v2 6/7] repo: add path.grafts with absolute and relative suffix formatting
From: K Jayatheerth @ 2026-07-21  2:19 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Lucas Seiki Oshiro, git, jltobler
In-Reply-To: <xmqq33xejomv.fsf@gitster.g>

Hey Lucas and Junio,

On Mon, Jul 20, 2026 at 9:31 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Lucas Seiki Oshiro <lucasseikioshiro@gmail.com> writes:
>
> >> Introduce `path.grafts.absolute` and `path.grafts.relative` keys to
> >> `git repo info`. This allows scripting layers to query the active grafts
> >> context cleanly while scaling transparently with active `GIT_GRAFT_FILE`
> >> environment variable overrides.
> >
> > I ran `git repo info path.grafts.relative` in a repository with no
> > `grafts` file, and it returned `.git/info/grafts`, which obviously
> > doesn't exist.
> >
> > Wouldn't it be better if we check if that file exists before
> > returning this value?
>
> That is an interesting question, but I think it depends on who is
> querying and for what purpose.
>
> If a script is asking where to write the file, then the author wants
> to know where the file is supposed to be, even if no such file
> exists yet.  Since the file format is public, they are free to write
> their own tools to manipulate it.
>
> Thanks.

That's tough.
I think I align with Junio here.

I just used rev-parse as my compass to work on this command.
Just to clarify
Should I send a v3 changing something?

Regards,
- K Jayatheerth

^ permalink raw reply

* Re: [PATCH v10 0/5] history: add squash subcommand to fold a range
From: Matt Hunter @ 2026-07-21  1:33 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget, git
  Cc: Phillip Wood, D. Ben Knoble, Patrick Steinhardt, Harald Nordgren
In-Reply-To: <pull.2337.v10.git.git.1784536024.gitgitgadget@gmail.com>

On Mon Jul 20, 2026 at 4:26 AM EDT, Harald Nordgren via GitGitGadget wrote:
> Adds git history squash <revision-range> to fold a range of commits.
>
> Changes in v10:
>
>  * Record the full revision expression in squash reflog.
>  * Preserve the boundary-walk invariant when sanitizing rev-list options.
>  * Clarify amend! and --reedit-message documentation.
>

v10 looks good to me!
Thanks!

^ permalink raw reply

* Re: Bug report - git rev-list --exclude-first-parent-only [SEC=UNOFFICIAL]
From: Jerry Zhang @ 2026-07-21  0:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Michael Hore, git@vger.kernel.org
In-Reply-To: <xmqqbjcnizr1.fsf@gitster.g>

On Fri, Jul 3, 2026 at 1:28 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Michael Hore <Michael.Hore@asic.gov.au> writes:
>
> > I believe I have found a bug -
> >
> > My repo has a commit structure like
> >
> > R2
> > |\
> > | F
> > |/
> > R1
> >
> > i.e.
> >  - there is a merge commit R2 with parents R1 and F
> >  - the parent of F is R1
>
> IOW, R2 is a useless merge that could have been a simple
> fast-forward directly to F.
>
> > I ran "git rev-list --exclude-first-parent-only F ^R2"
> >
> > it gave the expected result: "F"
> >
> > I ran "git rev-list --exclude-first-parent-only F R1 ^R2"
> >
> > I expected the same result, but I got an unexpected result - nothing at all
>
> This seems to have come from 9d505b7b49 (git-rev-list: add
> --exclude-first-parent-only flag, 2022-01-11).  I do not know if the
> original author is still around, but it would have been nicer to ask
> for input from them (cc'ed).
>
> A fix could be something along this line, but I've never used this
> feature even once (I instead use Michael Haggerty's exellent "git
> when-merged" thing), so I may very well be breaking _other_ use
> cases this feature was originally intended for without knowing.
fwiw when-merged seems to be asking the question "when was X branch
merged into the
baseline", while exclude-first-parent-only is asking "when did X
branch first split off from
the baseline". of course that property may not be interesting to you
if you're looking for the
former.
>
> The patched part is inside a huge "while (parent)" loop.  The idea
> is to break out before the loop goes on to smudge later parents when
> we are in the "smudge only first parent as uninteresting, without
> contaminating the history leading to other parents" mode.
>
>  revision.c                   | 10 ++++++++--
>  t/t6012-rev-list-simplify.sh | 18 ++++++++++++++++++
>  2 files changed, 26 insertions(+), 2 deletions(-)
>
> diff --git c/revision.c w/revision.c
> index e91d7e1f11..1f50d42a7a 100644
> --- c/revision.c
> +++ w/revision.c
> @@ -1151,12 +1151,18 @@ static int process_parents(struct rev_info *revs, struct commit *commit,
>                         if (p)
>                                 p->object.flags |= UNINTERESTING |
>                                                    CHILD_VISITED;
> -                       if (repo_parse_commit_gently(revs->repo, p, 1) < 0)
> +                       if (repo_parse_commit_gently(revs->repo, p, 1) < 0) {
> +                               if (revs->exclude_first_parent_only)
> +                                       break;
>                                 continue;
> +                       }
>                         if (p->parents)
>                                 mark_parents_uninteresting(revs, p);
> -                       if (p->object.flags & SEEN)
> +                       if (p->object.flags & SEEN) {
> +                               if (revs->exclude_first_parent_only)
> +                                       break;
>                                 continue;
> +                       }
>                         p->object.flags |= (SEEN | NOT_USER_GIVEN);
>                         if (queue)
>                                 prio_queue_put(queue, p);
> diff --git c/t/t6012-rev-list-simplify.sh w/t/t6012-rev-list-simplify.sh
> index 4cecb6224c..2284bbba12 100755
> --- c/t/t6012-rev-list-simplify.sh
> +++ w/t/t6012-rev-list-simplify.sh
> @@ -285,4 +285,22 @@ test_expect_success 'log --graph --simplify-merges --show-pulls' '
>         test_cmp expect actual
>  '
>
> +test_expect_success 'exclude-first-parent-only with parent already seen' '
> +       git checkout --orphan test-seen &&
> +       git rm -rf . &&
> +       test_commit r1 &&
> +       git checkout -b branch-f &&
> +       test_commit f &&
> +       git checkout test-seen &&
> +       git merge --no-ff --no-edit -m r2 branch-f &&
> +       git tag r2 &&
> +
> +       git rev-list --exclude-first-parent-only f ^r2 >actual &&
> +       git rev-parse f >expect &&
> +       test_cmp expect actual &&
> +
> +       git rev-list --exclude-first-parent-only f r1 ^r2 >actual2 &&
> +       test_cmp expect actual2
> +'
> +
>  test_done
>
Its been a while since i've looked at the code, but the rationale and
test case make sense to me. thanks

Reviewed-by: Jerry Zhang <jerry@skydio.com>

^ permalink raw reply

* Re: [PATCH 2/2] stash: avoid sparse-index expansion for in-cone paths
From: Taylor Blau @ 2026-07-20 23:54 UTC (permalink / raw)
  To: tnyman; +Cc: git, Derrick Stolee, Taylor Blau, Jeff King, Victoria Dye
In-Reply-To: <20260720223118.62821-6-tnyman@openai.com>

On Mon, Jul 20, 2026 at 03:31:21PM -0700, tnyman@openai.com wrote:
> Signed-off-by: Ted Nyman <tnyman@openai.com>
> ---
>  builtin/stash.c                          |  4 +-
>  t/perf/p2000-sparse-operations.sh        |  1 +
>  t/t1092-sparse-checkout-compatibility.sh | 55 ++++++++++++++++++++++++
>  3 files changed, 58 insertions(+), 2 deletions(-)

All looks reasonable, and it's very nice indeed to see another one of
these /* TODO */ comments go away ;-).

Very pleasant read, this series is

    Reviewed-by: Taylor Blau <ttaylorr@openai.com>

, and looks good to me.

Thanks,
Taylor

^ permalink raw reply

* Re: [PATCH 1/2] pathspec: use match for sparse-index expansion checks
From: Taylor Blau @ 2026-07-20 23:53 UTC (permalink / raw)
  To: tnyman; +Cc: git, Derrick Stolee, Taylor Blau, Jeff King, Victoria Dye
In-Reply-To: <20260720223118.62821-5-tnyman@openai.com>

On Mon, Jul 20, 2026 at 03:31:20PM -0700, tnyman@openai.com wrote:
> Using `item.original + item.nowildcard_len` in
> `pathspec_needs_expanded_index()` can therefore read past the end of
> the allocation. AddressSanitizer reports a heap-buffer-overflow for
> prefixed wildcard pathspecs passed to `git rm` and `git reset` with a
> sparse index.
>
> The mismatch dates back to 4d1cfc1351 ("reset: make --mixed
> sparse-aware", 2021-11-29), which introduced the helper using
> `item.original`. b29ad38322 ("pathspec.h: move
> pathspec_needs_expanded_index() from reset.c to here", 2022-08-07)
> later moved it to `pathspec.c` and preserved the affected comparisons.

Nice find. I can reliably reproduce the ASan failure you described above
like so:

    repo=$(mktemp -d /tmp/pathspec-asan.XXXXXX)
    trap 'rm -rf "$repo"' EXIT

    git init "$repo"

    cd "$repo"

    mkdir -p deep outside
    : >deep/a
    : >outside/file
    git add .
    git commit -q -m base

    git sparse-checkout init --cone --sparse-index
    git sparse-checkout set deep

    # From deep/, match is "deep/a*" while original is only "a*".
    git.compile -C deep reset HEAD -- 'a*'

(where 'git.compile' points at my build, which in this case was compiled
with "make SANITIZE=address"), and results in

    ==89470==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000001f36 at pc 0x000106a69430 bp 0x00016b07c530 sp 0x00016b07bce0
    READ of size 1 at 0x602000001f36 thread T0
        #0 0x000106a6942c in strspn+0x3f0 (libclang_rt.asan_osx_dynamic.dylib:arm64e+0x1942c)
        #1 0x0001054a35b0 in pathspec_needs_expanded_index pathspec.c:850
        #2 0x000104fe6c24 in read_from_tree reset.c:214
        #3 0x000104fe5774 in cmd_reset reset.c:495
    [...]

It made me wonder whether or not this bug was trigger-able back in
4d1cfc1351. After checking out that version, I re-ran the same script
and got an identical buffer overflow in the 'strspn()' call.

Applying your patch and repeating the same steps results in a clean
exit.

> diff --git a/pathspec.c b/pathspec.c
> index f78b22709ccb67..281858f21f9c59 100644
> --- a/pathspec.c
> +++ b/pathspec.c
> @@ -847,9 +847,9 @@ int pathspec_needs_expanded_index(struct index_state *istate,
>  			 * - not-in-cone/bar*: may need expanded index
>  			 * - **.c: may need expanded index
>  			 */
> -			if (strspn(item.original + item.nowildcard_len, "*") ==
> +			if (strspn(item.match + item.nowildcard_len, "*") ==

OK. The comment above is elided from the diff context, but is useful
IMHO during review. Here we want to make sure that the remaining
wildcard-ed portion of the pathspec element is only "*", which may need
to expand the index only if we are not inside of the existing sparse
checkout.

But 'item.nowildcard_len' bytes ahead of 'item.original' may (at worst)
point into uninitialized memory, or (at best) point at a portion of the
string that is not in fact a wildcard (even if the pathspec item would
not otherwise require us to expand the sparse checkout).

So this makes sense.

>  				    (unsigned int)(item.len - item.nowildcard_len) &&
> -			    path_in_cone_mode_sparse_checkout(item.original, istate))
> +			    path_in_cone_mode_sparse_checkout(item.match, istate))

Likewise. Here I think we *might* actually be OK, but I haven't read
'path_in_cone_mode_sparse_checkout()' to know whether that's (a) true,
and (b) if so, whether it's true by accident or intention.

Regardless, 'item.match' makes sense here as well for the same reason.
Likewise with the rest of the patch.

> diff --git a/t/t1092-sparse-checkout-compatibility.sh b/t/t1092-sparse-checkout-compatibility.sh
> index 9814431cd74aff..d0b42371663f9d 100755
> --- a/t/t1092-sparse-checkout-compatibility.sh
> +++ b/t/t1092-sparse-checkout-compatibility.sh
> @@ -2119,6 +2119,13 @@ test_expect_success 'sparse index is not expanded: rm' '
>  	ensure_not_expanded rm -r deep
>  '
>
> +test_expect_success 'sparse index is not expanded: prefixed wildcard pathspec' '
> +	init_repos &&
> +
> +	ensure_not_expanded -C deep rm --dry-run -- "a*" &&
> +	ensure_not_expanded -C deep reset base -- "a*"
> +'

Looks good, this is effectively the same thing as I ran in the
reproduction script above.

Thanks,
Taylor

^ permalink raw reply

* Re: [PATCH 2/2] remote: resolve URL-valued push tracking remotes
From: Junio C Hamano @ 2026-07-20 23:49 UTC (permalink / raw)
  To: Harald Nordgren; +Cc: Harald Nordgren via GitGitGadget, git
In-Reply-To: <CAHwyqnV=ZbthekwTcmrK5twCOgNETW+0Z5uj=w3oKjUK6Hv47g@mail.gmail.com>

Harald Nordgren <haraldnordgren@gmail.com> writes:

> Thanks for your continued support on all my topics!
>
> Yes, I should clarify in the commit message what the actual motivation
> is, which is for me to handle remote renames in a smoother way, since
> 'gh' renmames remotes when forking a repo which is messing with
> @{push} and compareBranches for 'git status'.


Yeah, it would be a good thing to do in an updated version.

Thanks.

^ permalink raw reply

* Re: [PATCH RFC v3 2/2] Move libgit.a sources into separate "lib/" directory
From: Taylor Blau @ 2026-07-20 23:40 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: brian m. carlson, Patrick Steinhardt, git, Elijah Newren,
	Derrick Stolee, SZEDER Gábor, Johannes Schindelin,
	Phillip Wood
In-Reply-To: <xmqqqzkx9t95.fsf@gitster.g>

On Mon, Jul 20, 2026 at 03:43:50PM -0700, Junio C Hamano wrote:
> I do not think we want to do this in a single large change.  If we
> were to move everything to 'lib/' only to then need to further group
> them into subdirectories of 'lib/', it would subject us to multiple
> rounds of disruption.  I suspect it would be far less disruptive if
> we migrated one subsystem at a time, directly to a new directory
> immediately below the root level.

I agree.

Though it may seem *more* disruptive to do it piecemeal instead of all
at once, I think it would be preferable to avoid having a single
subsystem have to move multiple times.

That said, I am not sure that I completely understand the motivation
behind such a change to begin with. The second patch in this series
claims that:

 - "The Git project is not exactly the easiest project to get started in
   [...]", because in part:

 - "[..] finding your way around in our project's tree is not easy.
   Doing a directory listing in the top-level directory will present you
   with more than 550 files, which makes it extremely hard for a
   newcomer to figure out what files they are even supposed to look at."

I am not sure I understand how moving ~700 some odd files into "lib" makes
the project easier to navigate. I understand the patch's latter point
that:

 - "It is not obvious at all which files are part of "libgit.a" and
   which files are only linked into our final executables."

But don't see how this distinction will help newcomers who are likely
not yet thinking about which files are part of libgit.a and which are
not.

My other thought is that I worry that "lib" might itself be somewhat
misleading, given that many of the files being moved are not especially
amenable in the current form to being linked against as external
libraries.

So, I guess my feeling is that I am not closed off to the idea that the
benefits outweigh the risks/drawbacks here, but I currently do not see
that they do.

Thanks,
Taylor

^ permalink raw reply

* Re: [PATCH RFC v3 2/2] Move libgit.a sources into separate "lib/" directory
From: Junio C Hamano @ 2026-07-20 22:43 UTC (permalink / raw)
  To: brian m. carlson
  Cc: Patrick Steinhardt, git, Elijah Newren, Derrick Stolee,
	SZEDER Gábor, Johannes Schindelin, Phillip Wood
In-Reply-To: <al6Yz_QMlyU1GETv@fruit.crustytoothpaste.net>

"brian m. carlson" <sandals@crustytoothpaste.net> writes:

> I would very much welcome better rename support and I'm sure the
> community would as well.  If we can incentivize ourselves to step up and
> implement that, I'm all for it.

I would welcome such an effort.  It is, however, a different story
to move things simply because we want to move them, without a
concrete need or strategy to do so.

In any case, the root level of the 'lib/' directory introduced by
the 'ps/libgit-in-subdir' topic is full of source files, with only a
small number of focused subdirectories like 'odb/', 'refs/', and
'ewah/' mixed in to house specific subsystems.  This merely shifts
the clutter one level down without resolving it.

I would rather see a structure where each subsystem-like group
carves out its own directory.

I do not particularly care whether such a directory lives at the
root level or inside 'lib/'.  But if we were to establish a sensible
grouping, I suspect we would not need a 'lib/' directory solely to
house the 'refs/' and 'odb/' subdirectories.  Instead, it would be
sufficiently clean to have 'refs/', 'odb/', and other subsystem
directories directly under the root level.

I do not think we want to do this in a single large change.  If we
were to move everything to 'lib/' only to then need to further group
them into subdirectories of 'lib/', it would subject us to multiple
rounds of disruption.  I suspect it would be far less disruptive if
we migrated one subsystem at a time, directly to a new directory
immediately below the root level.

^ 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