Git development
 help / color / mirror / Atom feed
* Re: [PATCH 1/2] branch: suggest <remote>/<branch> on upstream slip
From: Junio C Hamano @ 2026-06-22 19:56 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
In-Reply-To: <21684539debaf433b6b63404e1a7622a5cc33283.1781262619.git.gitgitgadget@gmail.com>

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

> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> "git branch --set-upstream-to origin main" reads the trailing word as
> the local branch to operate on and dies with "branch 'main' does not
> exist", pointing at the wrong problem.

When 'main' does not exist locally,

    $ git branch --set-upstream-to "$anything" main

would fail before even looking at the "$anything" (which is supposed
to specify the new_upstream for the named local branch 'main').  The
operation is to set the upstream for 'main', and if 'main' does not
exist, doesn't the user deserve the error that says 'main' does not
exist, no matter what "$anything" is, whether it is a well-formed or
ill-formed remote tracking branch name?

So it is unclear, at least to me, why "branch 'main' does not exist"
is an inappropriate message, mostly because these three lines does
not clearly tell me what the user _expected_ the command line to do.

When 'main' does exist, but named upstream "$anything" does not, we
get

    $ git branch sample master ;# make sure the thing exists
    $ git branch --set-upstream-to origin sample
    fatal: the requested upstream branch 'origin' does not exist
    hint:
    hint: If you are planning on basing your work on an upstream
    hint: branch that already exists at the remote, you may need to
    hint: run "git fetch" to retrieve it.
    hint:
    hint: If you are planning to push out a new local branch that
    hint: will track its remote counterpart, you may want to use
    hint: "git push -u" to set the upstream config as you push.
    hint: Disable this message with "git config set advice.setUpstreamFailure false"

which does sound clear enough to me, even though it does not exactly
say "Even though upstream branch 'origin' does not exist, 'origin'
is a nickname for a remote, perhaps you meant to say
origin/something?"

I do not doubt you are trying to address a real issue, but the above
three-line description does not tell me what that problem is.

Now I do not regularly use --set-upstream-to, so I may be missing an
obvious common mistake modes, but a couple of my attempts to make
bad command invocations seem to give me reasonable responses:

    $ git branch --set-upstream-to ko/master sample
    branch 'sample' set up to track 'ko/master'.

OK, both are well formed so no problem.

    $ git branch --set-upstream-to ko/mastre sample
    fatal: the requested upstream branch 'ko/mastre' does not exist
    hint:
    hint: If you are planning on basing your work on an upstream
    hint: branch that already exists at the remote, you may need to
    hint: run "git fetch" to retrieve it.
    hint:
    hint: If you are planning to push out a new local branch that
    hint: will track its remote counterpart, you may want to use
    hint: "git push -u" to set the upstream config as you push.
    hint: Disable this message with "git config set advice.setUpstreamFailure false"

Misspelt upstream branch name diagnosed correctly, just like the
case where I gave 'origin', which does not exist, either.

^ permalink raw reply

* Re: [PATCH/RFC 2/6] commit-reach: introduce struct paint_queue with per-side counters
From: Derrick Stolee @ 2026-06-22 20:23 UTC (permalink / raw)
  To: Kristofer Karlsson
  Cc: Kristofer Karlsson via GitGitGadget, git, Elijah Newren
In-Reply-To: <CAL71e4Pcw-UUbHBw_j6PFx2bXmxZ93VLMWG+3Qap=RmCJa_ZgA@mail.gmail.com>

On 6/22/2026 3:14 PM, Kristofer Karlsson wrote:
> On Mon, 22 Jun 2026 at 20:10, Derrick Stolee <stolee@gmail.com> wrote:
>>

>> Also: technically "case 0" should be a BUG() state, right? We
>> shouldn't be walking any commit that isn't reachable from at
>> least one side. (case 0 does happen for old_paint, though.)
> 
> No, this is actually intended - initially I started with skipping
> case 0 and let it fall through, but that would hide _other_ bugs.
> I use 0 as a marker for "not in the queue" so we have this:
> Enqueuing: 0 -> flags
> Dequeueing: flags -> 0
> Only the case with the modified commit being in the queue
> will have non-zero flags. I tried to document this, but perhaps
> it is not clear enough, I will see if I can rephrase it, or add an
> inline comment around the case itself.

I bet this would be obvious if I tried to change the code and
run the tests. thanks for the explanation.

>>> +     while ((commit = paint_queue_get(&queue))) {
>> ...> +
>>> +             if (queue.p1_count + queue.p2_count +
>>> +                 queue.pending_merge_bases == 0)
>>> +                     break;
>>>       }
>> When possible, I like to try to make loops only have one terminating
>> condition. Should we have paint_queue_get() return NULL when it sees
>> this internal state condition?
> 
> Possibly, but that would couple the paint_queue struct very tightly with
> the usage. Not a problem in practice since it only has one call site, and
> it's unlikely that we want to add more of them but it may feel more natural
> to let the paint_queue purely have the queue semantics and counters,
> and keep the halt condition within the function itself. I don't feel
> super-strongly about this and can change it if needed, I will just need to
> verify that nothing else gets complex as a result, I have not fully thought
> through the effects.

Hm. Interesting. The coupling is perhaps expected, because the data
structure tracks counts that don't otherwise need to be tracked.
Maybe the terminating condition method could be descriptively named
to say why it would be completing.

>> Also, I'd rather see it of the form of (!count) instead of using
>> addition to make it clear that we care about each value being zero.
> 
> I did consider that, and most of the code in commit-reach.c at least
> prefers x and !x over x != 0 and x == 0, but my thinking was that
> other code in the repo did use comparison operators specifically
> for things like counters. Happy to change it to conform better though!
I just worry about the idea that a negative number (or an addition
overflow) would create conditions for termination that we did not
intend. That's why using the nonzero status as true/false combined
with ands and ors is better.

>> Finally, I think we actually want this case to get the benefit:
>>
>>         if ((!queue.p1_count || !queue.p2_count) &&
>>             !queue.pending_merge_bases)
>>
>> I do see that you have this condition in patch 3 with the extra
>> detail that the max generation in the queue is finite. I think this
>> is more reason to include this in the data structure method and not
>> in the loop.
> 
> Yes, but just to be clear, you don't want to merge together patch 2 and 3
> here, just grouping the halt conditions closer together
> (within paint_queue_get)? Keeping patch 2 and 3 separate would be nice
> to make it easier to show that introducing this extra counter bookkeeping
> does not negatively impact the overall performance too much.
No, I don't want you to squash them. I was perhaps unclear as I was
discovering the structure as we went. The thing I was missing above
was the "finite generation number" condition, which you make very
clear in patch 3.

Thanks,
-Stolee



^ permalink raw reply

* Re: [PATCH/RFC 3/6] commit-reach: terminate merge-base walk when one paint side is exhausted
From: Derrick Stolee @ 2026-06-22 20:26 UTC (permalink / raw)
  To: Kristofer Karlsson
  Cc: Kristofer Karlsson via GitGitGadget, git, Elijah Newren
In-Reply-To: <CAL71e4NJZ9c_=0W4djRFCYPw4z_dkh_ZHEDWBk8cuwXhxT9jgw@mail.gmail.com>

On 6/22/2026 3:19 PM, Kristofer Karlsson wrote:

> I think I may need to create some type of (temporary, internal)
> test runner that runs the same walk multiple times to reduce
> the noise from parsing commits.
I've used hyperfine [1] when doing specific performance tests
in the past. You can build Git before and after and have hyperfine
run the two modes and compare them:

	hyperfine --warmup=3 \
		-n 'old' "~/git-old/bin-wrappers/git -C $repo merge-base $A $B" \
		-n 'new' "~/git-new/bin-wrappers/git -C $repo merge-base $A $B"

[1] https://github.com/sharkdp/hyperfine

Good luck!
-Stolee


^ permalink raw reply

* Re: [PATCH/RFC 4/6] t6600: add test cases for side-exhaustion edge cases
From: Derrick Stolee @ 2026-06-22 20:28 UTC (permalink / raw)
  To: Kristofer Karlsson; +Cc: Elijah Newren via GitGitGadget, git, Elijah Newren
In-Reply-To: <CAL71e4M0T4fFG4JuYTp_ZPHzNcHXf342Xkh0n0dt4LVKsuSu2Q@mail.gmail.com>

On 6/22/2026 3:25 PM, Kristofer Karlsson wrote:
> On Mon, 22 Jun 2026 at 20:15, Derrick Stolee <stolee@gmail.com> wrote:
>> It's usually my preference to see these tests show up before the
>> new code arrives, that way we can see that they already work with
>> the old logic and continue to work with the new logic.
>>
>> It's minor, but putting them after your code change may be adding
>> enforcement of a change of behavior.
> 
> Agreed, I actually also prefer that in practice so I am not
> sure why I ordered them this way - perhaps some attempt at
> making it easier to review (show the idea and change before
> the verification). I will reorder to put all new tests as the first commit
> (or second, if I will also introduce a status-quo technical first).
> 
>>
>> One thing that could be helpful here is to consider tracing a
>> count of "commits walked" in the merge-base code, then you could
>> have these tests demonstrate the performance benefit by checking
>> for that number changing.
> 
> Good idea, I actually had some of that locally when developing it,
> but I removed the ugly traces before submitting this. I will try to
> re-introduce that in a nice way. It would be neat to let tests
> inspect that side effect, though in the worst case that could make
> it fragile. At the very least it's good for human debugging though.

And to be clear, I'm suggesting using trace2_data_intmax() calls
to get structured data that can be parsed in the GIT_TRACE2_EVENT
logs during tests. It could also be picked up by teletry tools that
listen to trace2 output, if desired.

It will show up differently in GIT_TRACE2_PERF, but that's a nice
human-readable way to debug things.

>> In t6600, that tracing number would not be the same across the
>> three different data shapes (full graph, half graph, no graph) and
>> that could be valuable to demonstrate in tests.
> 
> Agreed, the number of commits visited would be more interesting
> than the relative performance numbers since it's an algorithmic
> change rather than a micro-optimization.
They are both interesting, but only the commit count can be
guaranteed rigorously in the test suite. It's possible that a
great improvement to such a trace doesn't result in great end-to-
end time improvement, but I believe that it is true in this case.

Thanks,
-Stolee

^ permalink raw reply

* Re: [PATCH 2/2] push: suggest <remote> <branch> for a slash slip
From: Junio C Hamano @ 2026-06-22 20:40 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
In-Reply-To: <ea1412b1107f485cf52c953e387a513d95d82b53.1781262619.git.gitgitgadget@gmail.com>

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

> From: Harald Nordgren <haraldnordgren@gmail.com>
>
> "git push origin/main" is treated as a repository and dies with
> "'origin/main' does not appear to be a git repository", with no hint
> that a space was meant instead of a slash.

This is easier for me to guess than what the user may have wanted to
do in the decription of [1/2].  But it still will be easier on
readers to say

    When pusing out up update the "main" branch to the remote
    "origin", i.e.,

        $ git push origin main

    it is easy for some users to mistakenly say

        $ git push origin/main

    instead.  This however instructs to push to remote "origin/main"
    with configured refspecs, which means a completely different
    thing.  Lucikly, often origin/main does not exist as a remote
    and the command fails without doing any harm, but still may
    leave the user puzzled what happened.  Give hint to ...

or something like that.

> When the argument is not an existing path or configured remote but its
> part before the first slash names one, suggest the intended
> "git push <remote> <branch>" form. The suggestion is shown as advice so
> it can be silenced with advice.pushRepoLooksLikeRef.

Sounds sensible.

>  	if (repo) {
>  		if (!add_remote_or_group(repo, &remote_group)) {
> +			const char *slash = strchr(repo, '/');
> +			struct remote *r;
> +
> +			/*
> +			 * A "<remote>/<branch>" argument that does not name
> +			 * a path is likely a slip for the separate
> +			 * "<remote> <branch>" form, so suggest that instead.
> +			 */
> +			if (slash && slash[1] && !file_exists(repo)) {
> +				struct strbuf name = STRBUF_INIT;
> +
> +				strbuf_add(&name, repo, slash - repo);
> +				if (remote_is_configured(remote_get(name.buf), 0)) {
> +					int code = die_message(_("'%s' is not a valid push target"), repo);
> +					advise_if_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF,
> +							  _("Did you mean to use: git push %s %s?"),
> +							  name.buf, slash + 1);
> +					strbuf_release(&name);
> +					exit(code);
> +				}
> +				strbuf_release(&name);
> +			}

Hmph, if this class of hint is not enabled, do we still have to
spend cycles on these "is this a remote?  is the first token a
remote?" computation?  I would have expected that a change here
would be a two-liner:

    if (!add_remote_or_group(...)) {
+	if (advise_enabled(ADVICE_PUSH_REPO_LOOKS_LIKE_REF))
+		die_if_plausible_typo(...);
	... do the "try treating it as a direct URL or path" thing ...
    }


with the bulk of the "if it has slash, it is not a file, then advise
and die" logic inside the new helper function.

What I find especially troubling is that even when advise for this
class of hint is not enabled, the new code will hit the new exit(),
without falling back to the "try treating it as a direct URL or
path" thing.  Or am I missing something?

Thanks.


^ permalink raw reply

* Re: [PATCH GSoC RFC v13 06/12] connect: refactor packet writing
From: Karthik Nayak @ 2026-06-22 20:43 UTC (permalink / raw)
  To: Pablo Sabater, gitster
  Cc: peff, eric.peijian, chriscool, git, jltobler, toon,
	chandrapratap3519, Jonathan Tan, Calvin Wan
In-Reply-To: <20260619-ps-eric-work-rebase-v13-6-3d4c7315d2f8@gmail.com>

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

Pablo Sabater <pabloosabaterr@gmail.com> writes:

[snip]

> diff --git a/connect.c b/connect.c
> index 1dced8e632..78c69d4485 100644
> --- a/connect.c
> +++ b/connect.c
> @@ -700,16 +700,16 @@ int server_supports(const char *feature)
>  	return !!server_feature_value(feature, NULL);
>  }
>
> -void write_fetch_command_and_capabilities(struct strbuf *req_buf,
> -					  const struct string_list *server_options)
> +void write_command_and_capabilities(struct strbuf *req_buf, const char *command,
> +				    const struct string_list *server_options)
>  {
>  	const char *hash_name;
>  	int advertise_sid;
>
>  	repo_config_get_bool(the_repository, "transfer.advertisesid", &advertise_sid);
>
> -	ensure_server_supports_v2("fetch");
> -	packet_buf_write(req_buf, "command=fetch");
> +	ensure_server_supports_v2(command);
> +	packet_buf_write(req_buf, "command=%s", command);
>  	if (server_supports_v2("agent"))
>  		packet_buf_write(req_buf, "agent=%s", git_user_agent_sanitized());
>  	if (advertise_sid && server_supports_v2("session-id"))
> @@ -727,7 +727,7 @@ void write_fetch_command_and_capabilities(struct strbuf *req_buf,
>  			die(_("mismatched algorithms: client %s; server %s"),
>  			    the_hash_algo->name, hash_name);
>  		packet_buf_write(req_buf, "object-format=%s", the_hash_algo->name);
> -	} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1_LEGACY) {
> +	} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {
>  		die(_("the server does not support algorithm '%s'"),
>  		    the_hash_algo->name);
>  	}

Why did we make this change? If the server doesn't support v2, then the
object format should be `GIT_HASH_SHA1_LEGACY`. While the value of it is
indeed `GIT_HASH_SHA1`, it indicates a scenario where there was no
option to select object hash, which is the scenario here.

If there is a reason to make such a change, perhaps we should highlight
this in the commit message.

> diff --git a/connect.h b/connect.h
> index c4f6ea4b0a..8f4c523892 100644
> --- a/connect.h
> +++ b/connect.h
> @@ -34,8 +34,12 @@ void check_stateless_delimiter(int stateless_rpc,
>  			       struct packet_reader *reader,
>  			       const char *error);
>
> +/*
> + * Writes a command along with the requested server capabilities/features into a
> + * request buffer.
> + */
>  struct string_list;

The comment should be above the function and not the forward
declaration.

While we're here, why not `#include "string-list.h"` and remove the
forward declaration, is there a circular dependency?

> -void write_fetch_command_and_capabilities(struct strbuf *req_buf,
> -					  const struct string_list *server_options);
> +void write_command_and_capabilities(struct strbuf *req_buf, const char *command,
> +				    const struct string_list *server_options);
>
>  #endif
> diff --git a/fetch-pack.c b/fetch-pack.c
> index 4a8a70b5f3..3d32114907 100644
> --- a/fetch-pack.c
> +++ b/fetch-pack.c
> @@ -1387,7 +1387,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
>  	int done_sent = 0;
>  	struct strbuf req_buf = STRBUF_INIT;
>
> -	write_fetch_command_and_capabilities(&req_buf, args->server_options);
> +	write_command_and_capabilities(&req_buf, "fetch", args->server_options);
>
>  	if (args->use_thin_pack)
>  		packet_buf_write(&req_buf, "thin-pack");
> @@ -2255,7 +2255,7 @@ void negotiate_using_fetch(const struct oid_array *negotiation_restrict_tips,
>  					   the_repository, "%d",
>  					   negotiation_round);
>  		strbuf_reset(&req_buf);
> -		write_fetch_command_and_capabilities(&req_buf, server_options);
> +		write_command_and_capabilities(&req_buf, "fetch", server_options);
>
>  		packet_buf_write(&req_buf, "wait-for-done");
>
>
> --
> 2.54.0

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

^ permalink raw reply

* Re: [PATCH/RFC 3/6] commit-reach: terminate merge-base walk when one paint side is exhausted
From: Kristofer Karlsson @ 2026-06-22 21:03 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: Kristofer Karlsson via GitGitGadget, git, Elijah Newren
In-Reply-To: <4f9cae3c-5cef-420b-954b-d1981d9d5a67@gmail.com>

On Mon, 22 Jun 2026 at 22:26, Derrick Stolee <stolee@gmail.com> wrote:
>
> I've used hyperfine [1] when doing specific performance tests
> in the past. You can build Git before and after and have hyperfine
> run the two modes and compare them:
>
>         hyperfine --warmup=3 \
>                 -n 'old' "~/git-old/bin-wrappers/git -C $repo merge-base $A $B" \
>                 -n 'new' "~/git-new/bin-wrappers/git -C $repo merge-base $A $B"
>
> [1] https://github.com/sharkdp/hyperfine

I can definitely use that, but I was thinking that the overhead
of operations such as repo_parse_commit would be high relative
to the overhead of the new paint_queue struct such that it would
be hard to properly measure and that it would be easier if I could
spread out that cost across multiple internal runs (which requires
a custom binary of some sort), but perhaps it's enough to just
show that there's no measurable regression here and then
hyperfine is indeed the right fit. I'll start with that and see if I need
to do anything more complex.

Thanks,
Kristofer

^ permalink raw reply

* Re: [PATCH 0/2] branch/push: suggest intended form when remote/branch slip given
From: Junio C Hamano @ 2026-06-22 21:16 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
In-Reply-To: <pull.2331.git.git.1781262619.gitgitgadget@gmail.com>

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

> When the repository or upstream argument is a slip like "origin/main" or
> "origin main", suggest the intended "git push origin main" or "git branch
> --set-upstream-to=origin/main" form instead of failing with an unrelated
> error.

Sorry for asking a question that may be stupid, but what does the
word "slip" mean in the context of the above sentence?  I am having
a hard time coming up with a topic name while queuing these two
patches (an obvious candidate is hn/branch-push-slip-advise but I do
not know how well the word sits there).

Thanks.

^ permalink raw reply

* Re: [PATCH 1/2] branch: suggest <remote>/<branch> on upstream slip
From: Junio C Hamano @ 2026-06-22 21:35 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget; +Cc: git, Harald Nordgren
In-Reply-To: <xmqq1pdytkmj.fsf@gitster.g>

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

> "Harald Nordgren via GitGitGadget" <gitgitgadget@gmail.com> writes:
>
>> From: Harald Nordgren <haraldnordgren@gmail.com>
>>
>> "git branch --set-upstream-to origin main" reads the trailing word as
>> the local branch to operate on and dies with "branch 'main' does not
>> exist", pointing at the wrong problem.
>
> When 'main' does not exist locally,
>
>     $ git branch --set-upstream-to "$anything" main
>
> would fail before even looking at the "$anything" (which is supposed
> to specify the new_upstream for the named local branch 'main').  The
> operation is to set the upstream for 'main', and if 'main' does not
> exist, doesn't the user deserve the error that says 'main' does not
> exist, no matter what "$anything" is, whether it is a well-formed or
> ill-formed remote tracking branch name?
>
> So it is unclear, at least to me, why "branch 'main' does not exist"
> is an inappropriate message, mostly because these three lines does
> not clearly tell me what the user _expected_ the command line to do.

After pondering on this a bit, I _think_ (but I am guessing, and
your job as an author of proposed commit log message is to make sure
your readers do not have to guess) what the user expected was to set
the upstream for the currrent branch.

    When trying to set the upstream for the current branch to "main"
    branch of the remote "origin", i.e.,

        $ git branch --set-upstream-to origin/main

    it is easy for some users to mistakenly say

        $ git branch --set-upstream-to origin main

    But it is a request to set the upstream for the local branch
    "main" to "origin", which is not expected to work as the
    upstream most likely would look like <remote>/<branch> (e.g.,
    "origin/main").  The user would get either one of these errors:

        fatal: branch 'main' does not exist
        fatal: the requested upstream branch 'origin' does not exist

    Give a hint that we _suspect_ the user may have meant to set the
    upstream of the current branch to 'origin/main' (but do so only
    when 'origin/main' does exist), and tell them the right way to
    spell that request.

or something perhaps?

^ permalink raw reply

* Re: [PATCH v3 2/2] status: improve rebase todo list parsing
From: Junio C Hamano @ 2026-06-22 21:43 UTC (permalink / raw)
  To: Phillip Wood; +Cc: git, Elijah Newren, Patrick Steinhardt
In-Reply-To: <b3514e9b1c9515bf1a7f7983b9f120d63edba97f.1782117361.git.phillip.wood@dunelm.org.uk>

Phillip Wood <phillip.wood123@gmail.com> writes:

> +	if (!sequencer_parse_todo_command((const char**)&p, &cmd))

Style.  Missing SP between "char" and "**".


^ permalink raw reply

* Re: [PATCH 5/7] line-log: support diff stat formats with -L
From: Michael Montalbo @ 2026-06-23  2:25 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Michael Montalbo via GitGitGadget, git, D. Ben Knoble
In-Reply-To: <xmqq8q8bpl03.fsf@gitster.g>

On Thu, Jun 18, 2026 at 3:00 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> If "range-scoped" is a widely known term (as opposed to a new word
> invented only during the introduction of this topic), the above
> reads well with a nice rhythm, but otherwise it may be easier to
> read, i.e., something like
>
>         The stat formats counts only lines within the tracked range.
>
> without having readers learn yet another new term that is only used
> here.
>

It was something I invented for the topic, but I agree it is better to
avoid coining a new term, especially since it ends up being spelled
out anyway in the blurb that follows. Will replace the term.

> > diff --git a/diff.c b/diff.c
> > index 6233a96bf0..026fafeb90 100644
> > --- a/diff.c
> > +++ b/diff.c
> > @@ -4289,7 +4289,18 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
> >               xecfg.ctxlen = o->context;
> >               xecfg.interhunkctxlen = o->interhunkcontext;
> >               xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
> > -             if (xdi_diff_outf(&mf1, &mf2, NULL,
> > +
> > +             if (p->line_ranges) {
> > +                     struct line_range_filter lr_filter;
> > +
> > +                     line_range_filter_init(&lr_filter, p->line_ranges,
> > +                                            diffstat_consume, diffstat);
> > +
> > +                     if (line_range_filter_diff(&lr_filter, &mf1, &mf2,
> > +                                                &xpp, &xecfg))
> > +                             die("unable to generate diffstat for %s",
> > +                                 one->path);
> > +             } else if (xdi_diff_outf(&mf1, &mf2, NULL,
> >                                 diffstat_consume, diffstat, &xpp, &xecfg))
> >                       die("unable to generate diffstat for %s", one->path);
>
> It is pleasing to see that this can be done with such a surprisingly
> small change.
>

Agreed! I didn't initially plan it out that way during the first series,
but things fell into place nicely by the end.

^ permalink raw reply

* What's cooking in git.git (Jun 2026, #08)
From: Junio C Hamano @ 2026-06-23  7:06 UTC (permalink / raw)
  To: git

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

Git 2.55-rc2 will be tagged tomorrow.  There are a few topics I want
to merge to 'master' before it happens, but other than these topics,
I expect nothing will move to 'master' before the final.  The tree
is feature-frozen, and remaining topics in 'next' will stay in "Will
cook in 'next'" instead of "Will merge to 'master'" state.  We'd
want to force ourselves to concentrate on addressing topics that are
important fixes but still in the "Needs review" state, and of
course, find any correct any regressions relative to Git 2.54, until
we are ready to tag Git 2.55 final.

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

With maint, master, next, seen, todo:

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

With all the integration branches and topics broken out:

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

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

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

Release tarballs are available at:

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

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

* js/objects-larger-than-4gb-on-windows-more (2026-06-15) 7 commits
  (merged to 'next' on 2026-06-18 at 2b3ac350e6)
 + odb: use size_t for object_info.sizep and the size APIs
 + packfile,delta: drop the `cast_size_t_to_ulong()` wrappers
 + pack-objects: use size_t for in-core object sizes
 + packfile: widen unpack_entry()'s size out-parameter to size_t
 + pack-objects(check_pack_inflate()): use size_t instead of unsigned long
 + patch-delta: use size_t for sizes
 + compat/msvc: use _chsize_s for ftruncate

 source: <pull.2137.v2.git.1781524349.gitgitgadget@gmail.com>


* kw/gitattributes-typofix (2026-06-15) 1 commit
  (merged to 'next' on 2026-06-17 at 14ff167ef8)
 + gitattributes: fix eol attribute for Perl scripts

 source: <pull.2151.v2.git.1781510039164.gitgitgadget@gmail.com>

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

* kk/merge-base-exhaustion (2026-06-20) 6 commits
 - Documentation/technical: add paint-down-to-common doc
 - t6099, t6600: add side-exhaustion regression tests
 - t6600: add test cases for side-exhaustion edge cases
 - commit-reach: terminate merge-base walk when one paint side is exhausted
 - commit-reach: introduce struct paint_queue with per-side counters
 - commit-reach: decouple ahead_behind from nonstale_queue

 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.

 Expecting a reroll.
 cf. <CAL71e4Pcw-UUbHBw_j6PFx2bXmxZ93VLMWG+3Qap=RmCJa_ZgA@mail.gmail.com>
 source: <pull.2149.git.1781951820.gitgitgadget@gmail.com>


* dk/meson-enable-use-nsec-build (2026-06-20) 1 commit
 - meson: wire up USE_NSEC build knob

 The USE_NSEC build knob, which enables support for sub-second file
 timestamp resolution, has been wired up to the Meson build system.

 Waiting for response(s) to review comment(s).
 cf. <ajjuoS5Qc3K0nCRl@pks.im>
 source: <c4c5ade901ff95b0f95939ea818870e4f3d59da1.1781971201.git.ben.knoble+github@gmail.com>


* js/win32-localtime-r (2026-06-22) 1 commit
  (merged to 'next' on 2026-06-22 at 67d3fa726d)
 + win32: ensure that `localtime_r()` is declared even in i686 builds

 Build-fix for 32-bit Windows.

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


* ps/connected-generic-promisor-checks (2026-06-22) 4 commits
 - connected: search promisor objects generically
 - odb/source-packed: support flags when iterating an object prefix
 - odb/source-packed: extract logic to skip certain packs
 - Merge branch 'ps/odb-source-packed' into ps/connected-generic-promisor-checks
 (this branch uses ps/odb-source-packed.)

 The connectivity check has been refactored to search for promisor
 objects in a generic way using the object database interface,
 rather than iterating packfiles directly. This allows connectivity
 checks to work properly in repositories that do not use packfiles.

 Waiting for response(s) to review comment(s).
 cf. <xmqq4iiu1mrt.fsf@gitster.g>
 source: <20260622-pks-connected-generic-promisor-checks-v1-0-25eba2698202@pks.im>


* ps/libgit-in-subdir (2026-06-22) 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
 (this branch uses ps/odb-source-packed.)

 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.

 Needs review.
 source: <20260622-pks-libgit-in-subdir-v2-0-cb946c51ee7b@pks.im>


* ps/odb-generalize-prepare (2026-06-22) 3 commits
 - odb: introduce `odb_prepare()`
 - odb/source: generalize `reprepare()` callback
 - Merge branch 'ps/odb-source-packed' into ps/odb-generalize-prepare
 (this branch uses ps/odb-source-packed.)

 The `reprepare()` callback for object database sources has been
 generalized into a `prepare()` callback with an optional flush cache
 flag, and a new `odb_prepare()` wrapper has been introduced to
 allow pre-opening object database sources.

 Needs review.
 source: <20260622-b4-pks-odb-generalize-prepare-v1-0-d2a5c5d13144@pks.im>

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

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

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

 Waiting for response(s) to review comment(s) for too long, stalled.
 cf. <agrIrGwSMFlKTx9x@pks.im>
 source: <20260517132111.1014901-1-joerg@thalheim.io>


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

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

 Waiting for response(s) to review comment(s) for too long, stalled.
 cf. <xmqq33yzd9yf.fsf@gitster.g>
 cf. <SY0P300MB0801E50FCB7EB2F45CD15208CE042@SY0P300MB0801.AUSP300.PROD.OUTLOOK.COM>
 source: <SY0P300MB0801677A2A1E0FD38D06A841CE2A2@SY0P300MB0801.AUSP300.PROD.OUTLOOK.COM>


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

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

 Waiting for response(s) to review comment(s) for too long, stalled.
 cf. <xmqq8q97et9b.fsf@gitster.g>
 source: <20260525-includeif-worktree-v5-0-1efe525d025a@black-desk.cn>

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

* jc/submittingpatches-design-critiques (2026-06-20) 1 commit
 - SubmittingPatches: address design critiques

 The documentation in SubmittingPatches has been updated to clarify how
 patch contributors should respond to design and viability critiques,
 and how the resolution of such critiques should be recorded in the
 final commit messages.

 Will merge to 'next'.
 cf. <ajjwYGWZ6hQWr600@pks.im>
 source: <xmqqeci0g4mz.fsf@gitster.g>


* wy/doc-clarify-review-replies (2026-06-21) 2 commits
 - doc: advise batching patch rerolls
 - doc: encourage review replies before rerolling

 Documentation on community contribution guidelines has been updated to
 encourage replying to review comments before rerolling, and to advise
 a default limit of at most one reroll per day to give reviewers across
 different time zones enough time to participate.

 Needs review.
 source: <cover.1782028813.git.wy@wyuan.org>


* ps/gitlab-ci-windows (2026-06-15) 1 commit
  (merged to 'next' on 2026-06-22 at 6d177c61ea)
 + gitlab-ci: migrate Windows builds away from Chocolatey

 Wean the Windows builds in GitLab CI procedure away from
 (unfortunately unreliable) Chocolatey to install dependencies.

 Will merge to 'master'.
 cf. <ajP5owy3r_GyuLqk@denethor>
 source: <20260615-b4-pks-gitlab-ci-drop-chocolatey-v1-1-51a6e7d5e388@pks.im>


* ty/migrate-ignorecase (2026-06-19) 2 commits
 - 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.

 Waiting for response(s) to review comment(s).
 cf. <xmqqjyrr7ipf.fsf@gitster.g>
 source: <20260619155152.642760-1-cat@malon.dev>


* mm/line-log-limited-ops (2026-06-18) 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

 "git log -L<range>:<path>" learned to limit various "diff" operations
 like --stat, --check, -G, to the specified range:path.

 Waiting for response(s) to review comment(s).
 cf. <xmqq8q8bpl03.fsf@gitster.g>
 source: <pull.2152.git.1781806593.gitgitgadget@gmail.com>


* hn/history-squash (2026-06-20) 4 commits
 - history: re-edit a squash with every message
 - 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,
 replaying any descendants on top.

 Waiting for response(s) to review comment(s).
 cf. <ajkijomPo_kXSXul@pks.im>
 source: <pull.2337.v4.git.git.1782021195.gitgitgadget@gmail.com>


* ps/t4216-tap-fix (2026-06-19) 1 commit
 - t4216: fix no-op test that breaks TAP output

 TAP output breakage fix.

 Waiting for response(s) to review comment(s).
 cf. <xmqqa4sqlchz.fsf@gitster.g>
 cf. <ajjBmi39IFJW5p5V@pks.im>
 source: <20260619-pks-t4216-drop-unused-prereq-v1-1-2ce0d7bea088@pks.im>


* hn/macos-linker-warning (2026-06-19) 1 commit
  (merged to 'next' on 2026-06-22 at 0e7f024ab5)
 + config.mak.uname: avoid macOS dup-library warning

 Xcode 15 and later has a linker set to complain when the same library
 archive is listed twice on the command line.  Squelch the annoyance.

 Will merge to 'master'.
 cf. <ajjspU7lJ01GgrBw@pks.im>
 source: <pull.2314.v3.git.git.1781901127385.gitgitgadget@gmail.com>


* mh/fetch-follow-remote-head-config (2026-06-19) 8 commits
  (merged to 'next' on 2026-06-22 at 423079e1c8)
 + fetch: fixup a misaligned comment
 + fetch: add configuration variable fetch.followRemoteHEAD
 + fetch: refactor do_fetch handling of followRemoteHEAD
 + fetch: return 0 on known git_fetch_config
 + fetch: rename function report_set_head
 + t5510: cleanup remote in followRemoteHEAD dangling ref test
 + doc: explain fetchRemoteHEADWarn advice
 + fetch: fixup set_head advice for warn-if-not-branch

 The `fetch.followRemoteHEAD` configuration variable has been added to
 provide a default for the per-remote `remote.<name>.followRemoteHEAD`
 setting.

 Will cook in 'next'.
 cf. <xmqqcxxp1j2t.fsf@gitster.g>
 source: <20260619094751.2996804-1-m@lfurio.us>


* ps/refs-writing-subcommands (2026-06-17) 5 commits
 - builtin/refs: add "rename" subcommand
 - builtin/refs: add "create" subcommand
 - builtin/refs: add "update" subcommand
 - builtin/refs: add "delete" subcommand
 - builtin/refs: drop `the_repository`

 The "git refs" toolbox has been extended with new "create", "delete",
 "update", and "rename" subcommands to create, delete, update, and
 rename references, respectively.

 Needs review.
 source: <20260617-pks-refs-writing-subcommands-v2-0-07f3d18336f9@pks.im>


* po/hash-object-size-t (2026-06-16) 6 commits
  (merged to 'next' on 2026-06-21 at b780a276b9)
 + hash-object: add a >4GB/LLP64 test case using filtered input
 + hash-object: add another >4GB/LLP64 test case
 + hash-object --stdin: verify that it works with >4GB/LLP64
 + hash algorithms: use size_t for section lengths
 + object-file.c: use size_t for header lengths
 + hash-object: demonstrate a >4GB/LLP64 problem

 Support for hashing loose or packed objects larger than 4GB on Windows
 and other LLP64 platforms has been improved by converting object header
 buffers and data-handling functions from 'unsigned long' to 'size_t'.

 Will cook in 'next'.
 cf. <ajOQthRjhD3hRM9w@pks.im>
 source: <pull.2138.v2.git.1781621398.gitgitgadget@gmail.com>


* kh/submittingpatches-trailers (2026-06-18) 5 commits
  (merged to 'next' on 2026-06-22 at 2cd4a152c9)
 + SubmittingPatches: note that trailer order matters
 + SubmittingPatches: be consistent with trailer markup
 + SubmittingPatches: document Based-on-patch-by trailer
 + SubmittingPatches: discourage common Linux trailers
 + SubmittingPatches: encourage trailer use for substantial help

 The trailer sections in SubmittingPatches have been updated to
 encourage use of standard trailers.

 Will cook in 'next'.
 cf. <xmqq4ij0vo8f.fsf@gitster.g>
 source: <V3_CV_SubPatches_trailers.9ec@msgid.xyz>


* mv/log-follow-mergy (2026-06-21) 1 commit
 - log: improve --follow following renames for non-linear history

 "git log --follow" has been updated to handle non-linear history, in
 which the path being tracked gets renamed differently in multiple
 history lines, better.

 Will merge to 'next'.
 source: <ajjU4w2B0NlZffw1@collabora.com>


* wy/doc-myfirstcontribution-trim-quotes (2026-06-11) 1 commit
 - 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.

 Comments?
 cf. <xmqqcxxwljue.fsf@gitster.g>
 source: <080402ff0ac8127b654dccea59a1bf643df62a5c.1781186476.git.wy@wyuan.org>


* 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, and the pack
 exclusion logic incorrectly skipped packs from layers above the
 selected base, breaking reachability closure for bitmaps.

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


* mm/test-grep-lint (2026-06-12) 6 commits
 - 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

 Needs review.
 source: <pull.2135.v2.git.1781323575.gitgitgadget@gmail.com>


* rs/cat-file-default-format-optim (2026-06-14) 1 commit
  (merged to 'next' on 2026-06-17 at 43ed8b3969)
 + cat-file: speed up default format

 Will cook in 'next'.
 cf. <20260615165326.GA91269@coredump.intra.peff.net>
 source: <5a7ed929-6fe0-496c-83bd-65dee57c2241@web.de>


* kk/prio-queue-get-put-fusion (2026-06-08) 2 commits
 - prio-queue: fold lazy_queue into prio_queue for automatic get+put fusion
 - prio-queue: rename .nr to .nr_ and add accessor helpers

 The lazy priority queue optimization pattern (deferring actual removal
 in prio_queue_get() to allow get+put fusion) has been folded directly
 into prio_queue itself, speeding up commit traversal workflows and
 simplifying callers.

 Needs review.
 source: <pull.2140.v4.git.1780945851.gitgitgadget@gmail.com>


* td/ref-filter-memoize-contains (2026-06-12) 3 commits
 - 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.

 Needs review.
 source: <20260612-ref-filter-memoized-contains-v4-0-5ed39fd001dd@gmail.com>


* tc/replay-linearize (2026-06-22) 3 commits
 - replay: offer an option to linearize the commit topology
 - replay: add helper to put entry into mapped_commits
 - replay: refactor enum replay_mode into a bool

 git replay learns --linearize option to drop merge commits and
 linearize the replayed history, mimicking git rebase
 --no-rebase-merges.

 Waiting for response(s) to review comment(s).
 cf. <xmqq7bnq37jm.fsf@gitster.g>
 source: <20260622-toon-git-replay-drop-merges-v4-0-ff257f534319@iotcl.com>


* ps/setup-drop-global-state (2026-06-10) 8 commits
  (merged to 'next' on 2026-06-15 at d9a8b88d47)
 + treewide: drop USE_THE_REPOSITORY_VARIABLE
 + environment: stop using `the_repository` in `is_bare_repository()`
 + environment: split up concerns of `is_bare_repository_cfg`
 + builtin/init: stop modifying `is_bare_repository_cfg`
 + setup: remove global `git_work_tree_cfg` variable
 + builtin/init: simplify logic to configure worktree
 + builtin/init: stop modifying global `git_work_tree_cfg` variable
 + Merge branch 'ps/setup-centralize-odb-creation' into ps/setup-drop-global-state

 Continuation of "setup.c" refactoring to drop remaining global state
 (`git_work_tree_cfg`, `is_bare_repository_cfg`). The most notable
 outcome is that `is_bare_repository()` has been updated to no longer
 implicitly rely on `the_repository`.

 Will cook in 'next'.
 cf. <airVOrTboNDDGBak@denethor>
 cf. <87ldckyygk.fsf@emacs.iotcl.com>
 source: <20260611-b4-pks-setup-drop-global-state-v2-0-a6f7269c841d@pks.im>


* ps/refs-avoid-chdir-notify-reparent (2026-06-22) 12 commits
 - refs: protect against chicken-and-egg recursion
 - refs/reftable: lazy-load configuration to fix chicken-and-egg
 - reftable: split up write options
 - refs/files: lazy-load configuration to fix chicken-and-egg
 - refs: move parsing of "core.logAllRefUpdates" back into ref stores
 - repository: free main reference database
 - chdir-notify: drop unused `chdir_notify_reparent()`
 - refs: unregister reference stores from "chdir_notify"
 - setup: don't apply "GIT_REFERENCE_BACKEND" without a repository
 - setup: stop applying repository format twice
 - setup: inline `check_and_apply_repository_format()`
 - Merge branch 'ps/setup-centralize-odb-creation' into ps/refs-avoid-chdir-notify-reparent

 The reference backends have been converted to always use absolute
 paths internally. This allows dropping the calls to
 `chdir_notify_reparent()` and fixes a memory leak in how the
 reference database is constructed with an "onbranch" condition.

 Needs review.
 source: <20260622-b4-pks-refs-avoid-chdir-notify-reparent-v5-0-018475013dbc@pks.im>


* ps/odb-source-packed (2026-06-16) 18 commits
  (merged to 'next' on 2026-06-19 at dcf0c084e4)
 + odb/source-packed: drop pointer to "files" parent source
 + midx: refactor interfaces to work on "packed" source
 + odb/source-packed: stub out remaining functions
 + odb/source-packed: wire up `freshen_object()` callback
 + odb/source-packed: wire up `find_abbrev_len()` callback
 + odb/source-packed: wire up `count_objects()` callback
 + odb/source-packed: wire up `for_each_object()` callback
 + odb/source-packed: wire up `read_object_stream()` callback
 + odb/source-packed: wire up `read_object_info()` callback
 + packfile: use higher-level interface to implement `has_object_pack()`
 + odb/source-packed: wire up `reprepare()` callback
 + odb/source-packed: wire up `close()` callback
 + odb/source-packed: start converting to a proper `struct odb_source`
 + odb/source-packed: store pointer to "files" instead of generic source
 + packfile: move packed source into "odb/" subsystem
 + packfile: split out packfile list logic
 + packfile: rename `struct packfile_store` to `odb_source_packed`
 + Merge branch 'ps/odb-source-loose' into ps/odb-source-packed
 (this branch is used by ps/connected-generic-promisor-checks, ps/libgit-in-subdir and ps/odb-generalize-prepare.)

 The packed object source has been refactored into a proper struct
 odb_source.

 Will cook in 'next'.
 cf. <ajK2QKdW-TdflfR0@denethor>
 source: <20260617-pks-odb-source-packed-v3-0-b5c7583cd795@pks.im>


* td/ref-filter-restore-prefix-iteration (2026-06-12) 1 commit
  (merged to 'next' on 2026-06-19 at a19dbb4193)
 + ref-filter: restore prefix-scoped iteration

 Commands that list branches and tags (like git branch and git tag)
 have been optimized to pass the namespace prefix when initializing
 their ref iterator, avoiding a loose-ref scaling regression in
 repositories with many unrelated loose references.

 Will cook in 'next'.
 cf. <xmqqik7fsv2m.fsf@gitster.g>
 source: <20260612-fix-git-branch-regression-v4-1-f150038c02f4@gmail.com>


* ty/move-protect-hfs-ntfs (2026-06-20) 2 commits
  (merged to 'next' on 2026-06-20 at d8ca0d5180)
 + environment: use 'repo->initialized' for repo_protect_hfs() and repo_protect_ntfs()
  (merged to 'next' on 2026-06-15 at c2a30ca954)
 + environment: move 'protect_hfs' and 'protect_ntfs' into 'repo_config_values'

 The global configuration variables protect_hfs and protect_ntfs have
 been migrated into struct repo_config_values to tie them to
 per-repository configuration state.

 Will cook in 'next'.
 cf. <CAP8UFD35Tiy1_fqpjq8P-z=ZhzR3MTiThqfCs977652umRoSEQ@mail.gmail.com>
 cf. <xmqqse6uwdnz.fsf@gitster.g>
 source: <20260610124353.149874-2-cat@malon.dev>
 source: <20260620140957.667820-1-cat@malon.dev>


* ps/cat-file-remote-object-info (2026-06-19) 12 commits
 - cat-file: make remote-object-info allow-list dynamic
 - cat-file: validate remote atoms with allow_list
 - cat-file: add remote-object-info to batch-command
 - transport: add client support for object-info
 - serve: advertise object-info feature
 - fetch-pack: move fetch initialization
 - connect: refactor packet writing
 - fetch-pack: move function to connect.c
 - t1006: split test utility functions into new "lib-cat-file.sh"
 - cat-file: declare loop counter inside for()
 - git-compat-util: add strtoul_ul() with error handling
 - 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.

 The client dynamically filters format placeholders based on
 server-advertised capabilities and safely returns empty strings for
 inapplicable or unsupported fields.

 Waiting for response(s) to review comment(s).
 cf. <CAOLa=ZSvxXuf_bSzKMvViNQ5MuDAqxnQdo4asF9vfMhJaDQcVw@mail.gmail.com>
 source: <20260619-ps-eric-work-rebase-v13-0-3d4c7315d2f8@gmail.com>


* ap/http-redirect-wwwauth-fix (2026-06-02) 1 commit
 - http: preserve wwwauth_headers across redirects

 When cURL follows a redirect, the WWW-Authenticate headers from the
 redirect target were lost because credential_from_url() cleared the
 credential state. This has been fixed by preserving the collected
 headers across the redirect update.

 Expecting a reroll.
 cf. <5144a29d-a53f-4446-beff-e1f549345bf9@nvidia.com>
 source: <20260602161150.1527493-1-aplattner@nvidia.com>


* ps/doc-recommend-b4 (2026-06-15) 3 commits
  (merged to 'next' on 2026-06-17 at dd9a463369)
 + b4: introduce configuration for the Git project
 + MyFirstContribution: recommend the use of b4
 + MyFirstContribution: recommend shallow threading of cover letters

 Project-specific configuration for b4 has been introduced, and the
 documentation has been updated to recommend using it as a
 streamlined method for submitting patches.

 Will cook in 'next'.
 cf. <87eci7yomp.fsf@emacs.iotcl.com>
 source: <20260615-pks-b4-v4-0-22cfca8f19c5@pks.im>


* 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. This correctly
 skips aliases of the current branch and avoids duplicate updates for
 underlying real branches, fixing failures when branch aliases (like a
 default branch rename) are present.

 Waiting for response(s) to review comment(s).
 cf. <f982c386-e329-4ab0-b695-e540bcb9de3d@gmail.com>
 source: <pull.2126.v2.git.1780482436865.gitgitgadget@gmail.com>


* mm/diff-process-hunks (2026-06-14) 6 commits
 - 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

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

 Expecting a reroll.
 cf. <CAC2Qwm+P=fZOtpfMPeMiSXf3Afk6OLYpTP8Br78_PRA8WNL1Wg@mail.gmail.com>
 cf. <CAC2Qwm+P=fZOtpfMPeMiSXf3Afk6OLYpTP8Br78_PRA8WNL1Wg@mail.gmail.com>
 source: <pull.2120.v4.git.1781463564.gitgitgadget@gmail.com>


* tb/pack-path-walk-bitmap-delta-islands (2026-06-21) 5 commits
 - pack-objects: support `--delta-islands` with `--path-walk`
 - pack-objects: extract `record_tree_depth()` helper
 - pack-objects: support reachability bitmaps with `--path-walk`
 - t/perf: drop p5311's lookup-table permutation
 - Merge branch 'ds/path-walk-filters' into tb/pack-path-walk-bitmap-delta-islands

 The pack-objects command now supports using reachability bitmaps and
 delta-islands concurrently with the `--path-walk` option, allowing
 faster packaging by falling back to path-walk when bitmaps cannot
 fully satisfy the request.

 Will merge to 'next'.
 cf. <xmqqwlvq1qyy.fsf@gitster.g>
 source: <cover.1782082975.git.me@ttaylorr.com>


* ty/migrate-trust-executable-bit (2026-06-19) 3 commits
 - environment: move trust_executable_bit into repo_config_values
 - read-cache: move 'ce_mode_from_stat()' to 'read-cache.c'
 - read-cache: remove redundant extern declarations

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

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


* kk/prio-queue-cascade-sift (2026-06-01) 1 commit
 - prio-queue: use cascade-down for faster extract-min

 prio_queue_get() has been optimized by using a cascade-down approach
 (promoting the smaller child at each level and sifting up the last
 element from the leaf vacancy), which halves the number of comparisons
 per extract-min operation in the common case.

 Expecting a reroll.
 cf. <CAL71e4Ob-B5MJ5DPY+_tzpj6nyrbQ5WutxED2T93SWJV6kJGPA@mail.gmail.com>
 cf. <CAL71e4MYNiScZjTwkApjDAjRh2LM0_SP59h5HCTywV-Pua03tw@mail.gmail.com>
 source: <pull.2132.v2.git.1780301856444.gitgitgadget@gmail.com>


* jk/repo-info-path-keys (2026-06-20) 3 commits
 - repo: add path.gitdir with absolute and relative suffix formatting
 - repo: add path.commondir with absolute and relative suffix formatting
 - path: extract append_formatted_path() and use in rev-parse

 The "git repo info" command has been taught new keys to output both
 absolute and relative paths for "gitdir" and "commondir", supported by
 a new path-formatting helper extracted from "git rev-parse".

 Expecting a reroll.
 cf. <CA+rGoLcahV9pPqkSAKvz9o3g2cw2PsYXxzzwAC8XoseFzMB5rA@mail.gmail.com>
 source: <20260621055534.46798-1-jayatheerthkulkarni2005@gmail.com>


* ps/history-drop (2026-06-15) 10 commits
 - builtin/history: implement "drop" subcommand
 - builtin/history: split handling of ref updates into two phases
 - reset: stop assuming that the caller passes in a clean index
 - reset: allow the caller to specify the current HEAD object
 - reset: introduce ability to skip updating HEAD
 - reset: introduce dry-run mode
 - reset: modernize flags passed to `reset_working_tree()`
 - reset: rename `reset_head()`
 - reset: drop `USE_THE_REPOSITORY_VARIABLE`
 - read-cache: split out function to drop unmerged entries to stage 0

 The experimental "git history" command has been taught a new "drop"
 subcommand to remove a commit and replay its descendants onto its
 parent.

 Needs review.
 source: <20260615-b4-pks-history-drop-v6-0-2e329e536d78@pks.im>


* jk/setup-gitfile-diag-fix (2026-06-16) 1 commit
  (merged to 'next' on 2026-06-18 at b63b3d1f25)
 + read_gitfile(): simplify NOT_A_REPO error message

 A regression in the error diagnosis code for invalid .git files has
 been fixed, avoiding a potential NULL-pointer crash when reporting
 that a .git file does not point to a valid repository.

 Will cook in 'next'.
 cf. <xmqqjyry4hax.fsf@gitster.g>
 source: <20260616123516.GA2301231@coredump.intra.peff.net>


* 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 updates.

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


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

 The path completion for commands like `git rm` and `git mv`, is being
 updated to hide dotfiles by default, unless the user explicitly starts
 the path with a dot, matching standard shell-completion behavior.

 Waiting for response(s) to review comment(s).
 cf. <xmqq1pe0g08t.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

 The -m/-F/-c/-C options to supply commit log message from outside the
 editor are now supported 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)

 Doc update for "git replay" to actually refer to its configuration
 variables.

 Needs review.
 source: <V3_CV_doc_replay_config.780@msgid.xyz>


* hn/status-pull-advice-qualified (2026-05-21) 1 commit
  (merged to 'next' on 2026-06-15 at 898a4df940)
 + remote: qualify "git pull" advice for non-upstream compareBranches

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

 Will cook in 'next'.
 cf. <xmqq7bo6xuok.fsf@gitster.g>
 source: <pull.2301.v4.git.git.1779372367317.gitgitgadget@gmail.com>


* hn/branch-delete-merged (2026-06-22) 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

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

 Waiting for response(s) to review comment(s).
 cf. <cb6fcdfb-67b4-429d-b820-c4e623f28cfa@gmail.com>
 source: <pull.2285.v17.git.git.1782113388.gitgitgadget@gmail.com>


* cc/promisor-auto-config-url-more (2026-05-27) 8 commits
  (merged to 'next' on 2026-06-15 at d1c99e75cc)
 + doc: promisor: improve acceptFromServer entry
 + promisor-remote: auto-configure unknown remotes
 + promisor-remote: trust known remotes matching acceptFromServerUrl
 + promisor-remote: introduce promisor.acceptFromServerUrl
 + promisor-remote: add 'local_name' to 'struct promisor_info'
 + urlmatch: add url_normalize_pattern() helper
 + urlmatch: change 'allow_globs' arg to bool
 + t5710: simplify 'mkdir X' followed by 'git -C X init'

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

 Will cook in 'next'.
 cf. <877bo7294j.fsf@emacs.iotcl.com>
 cf. <xmqqh5naxwfc.fsf@gitster.g>
 source: <20260527140820.1438165-1-christian.couder@gmail.com>


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

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

 Needs review.
 source: <pull.2281.v14.git.git.1781786652.gitgitgadget@gmail.com>


* en/ort-harden-against-corrupt-trees (2026-06-13) 5 commits
  (merged to 'next' on 2026-06-18 at e51bee59ca)
 + cache-tree: fix verify_cache() to catch non-adjacent D/F conflicts
 + merge-ort: abort merge when trees have duplicate entries
 + merge-ort: free diff pairs queue in clear_or_reinit_internal_opts()
 + merge-ort: drop unnecessary show_all_errors from collect_merge_info()
 + merge-ort: propagate callback errors from traverse_trees_wrapper()

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

 Will cook in 'next'.
 cf. <xmqq5x3ldu4h.fsf@gitster.g>
 source: <pull.2096.v2.git.1781419047.gitgitgadget@gmail.com>


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

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

 Will merge to 'next'.
 cf. <xmqqechy1o7p.fsf@gitster.g>
 source: <cover.1782117361.git.phillip.wood@dunelm.org.uk>


* ps/shift-root-in-graph (2026-06-20) 3 commits
 - graph: indent visual root in graph
 - revision: add peek functions for lookahead
 - 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.

 Waiting for response(s) to review comment(s).
 The peek-ahead approach may need to be scratched.
 cf. <CAN5EUNSj-2hkEBF7N_M6RLsuujDNFNUF3w53zR7SN1_5i2BRyg@mail.gmail.com>
 cf. <CAL71e4OQ_kGb+UwHgikHG236-8BVtc7P9OdpV4i4UzYRCoPczw@mail.gmail.com>
 source: <20260620-ps-pre-commit-indent-v6-0-cdc6d8fd5fbc@gmail.com>

^ permalink raw reply

* Re: [PATCH 0/2] branch/push: suggest intended form when remote/branch slip given
From: Harald Nordgren @ 2026-06-23  7:35 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Harald Nordgren via GitGitGadget, git
In-Reply-To: <xmqqpl1is2bm.fsf@gitster.g>

By slip it meant mistake, so you can call it 'hn/branch-push-mistake-advise'


Harald

^ permalink raw reply

* Re: [PATCH 3/3] connected: search promisor objects generically
From: Christian Couder @ 2026-06-23  7:45 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <20260622-pks-connected-generic-promisor-checks-v1-3-25eba2698202@pks.im>

On Mon, Jun 22, 2026 at 10:50 AM Patrick Steinhardt <ps@pks.im> wrote:
>
> When performing connectivity checks we have to figure out whether any of
> the new objects are promisor objects, as we cannot assume full
> connectivity if so.
>
> This check is performed by iterating through all packfiles in the
> repository and searching each of them for the given object. Of course,
> this mechanism is quite specific to implementation details of the object
> database, as we assume that it uses packfiles in the first place.
>
> Refactor the logic so that we instead use `odb_for_each_object_ext()`
> with an object prefix filter and the `ODB_FOR_EACH_OBJECT_PROMISOR_ONLY`
> flag. This will yield all objects that have the exact object name and
> that are part of a promisor pack in a generic way.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
>  connected.c | 39 +++++++++++++++++++++++++--------------
>  1 file changed, 25 insertions(+), 14 deletions(-)
>
> diff --git a/connected.c b/connected.c
> index 7e26976832..9a666f0cdf 100644
> --- a/connected.c
> +++ b/connected.c
> @@ -11,6 +11,13 @@
>  #include "packfile.h"
>  #include "promisor-remote.h"
>
> +static int promised_object_cb(const struct object_id *oid UNUSED,
> +                             struct object_info *oi UNUSED,
> +                             void *payload UNUSED)
> +{
> +       return 1;
> +}
> +
>  /*
>   * If we feed all the commits we want to verify to this command
>   *
> @@ -46,6 +53,11 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
>         }
>
>         if (repo_has_promisor_remote(the_repository)) {
> +               struct odb_for_each_object_options opts = {
> +                       .flags = ODB_FOR_EACH_OBJECT_PROMISOR_ONLY,
> +                       .prefix_hex_len = the_repository->hash_algo->hexsz,
> +               };
> +
>                 /*
>                  * For partial clones, we don't want to have to do a regular
>                  * connectivity check because we have to enumerate and exclude
> @@ -54,31 +66,30 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
>                  * object is a promisor object. Instead, just make sure we
>                  * received, in a promisor packfile, the objects pointed to by
>                  * each wanted ref.
> -                *
> -                * Before checking for promisor packs, be sure we have the
> -                * latest pack-files loaded into memory.
>                  */
> -               odb_reprepare(the_repository->objects);

Like Junio, I am not sure it's correct to remove the
`odb_reprepare(the_repository->objects)` call.

I think it was added for good reasons in b739d971 (connected.c:
reprepare packs for corner cases, 2020-03-13) and I am not sure
odb_for_each_object_ext() is performing something similar.

At least the commit message should mention this change and explain a
bit why the reasons the call was added are not valid anymore.

>                 do {
> -                       struct packed_git *p;
> -
> -                       repo_for_each_pack(the_repository, p) {
> -                               if (!p->pack_promisor)
> -                                       continue;
> -                               if (find_pack_entry_one(oid, p))
> -                                       goto promisor_pack_found;
> +                       opts.prefix = oid;
> +
> +                       err = odb_for_each_object_ext(the_repository->objects,
> +                                                     NULL, promised_object_cb,
> +                                                     NULL, &opts);
> +                       if (err < 0)
> +                               break;
> +                       if (err > 0) {
> +                               err = 0;
> +                               continue;
>                         }
> +
>                         /*
>                          * Fallback to rev-list with oid and the rest of the
>                          * object IDs provided by fn.
>                          */
>                         goto no_promisor_pack_found;
> -promisor_pack_found:
> -                       ;
>                 } while ((oid = fn(cb_data)) != NULL);
> +
>                 if (opt->err_fd)
>                         close(opt->err_fd);
> -               return 0;
> +               return err;
>         }
>
>  no_promisor_pack_found:

These changes are difficult to understand as there are a number of
`goto`, `break`, `return`, etc involved.

I think it comes in the first place from check_connected() doing too
many things, and adding a preparatory commit to refactor it would
help.

For example the preparatory commit could move a lot of code from
check_connected() to the following new functions:

/*
 * Returns:
 *   1  = all wanted OIDs found in promisor packs: connected, done.
 *   0  = at least one OID not found: caller must fall back to rev-list.
 *  <0  = error.
 * On the fallback (0) return, *oid is left pointing at the first
 * not-found OID so the rev-list path can resume the iteration.
 */
static int check_connected_promisor(oid_iterate_fn fn, void *cb_data,
                                     const struct object_id **oid);

/*
 * In a non-promisor repo, pass the first OID as `oid`.
 * Otherwise pass the first not-found OID resumed from
 * check_connected_promisor() as `oid`.
 */
static int check_connected_rev_list(oid_iterate_fn fn, void *cb_data,
                                     struct check_connected_options *opt,
                                     const struct object_id *oid);

^ permalink raw reply

* [PATCH] gpg-interface: fix strip_cr_before_lf to only remove CR before LF
From: DSAntonio08 @ 2026-06-23  8:45 UTC (permalink / raw)
  To: git; +Cc: DSAntonio08

The remove_cr_after() function was stripping all CR characters
unconditionally, even lone \r not followed by \n. This is incorrect
as only \r\n sequences (Windows line endings) should be normalized.

Fix the loop condition to skip \r only when immediately followed by
\n, and rename the function to strip_cr_before_lf to reflect its
actual behavior. Update both call sites and their comments accordingly.
---
 gpg-interface.c | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/gpg-interface.c b/gpg-interface.c
index dafd5371fa..87ae6503da 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -989,17 +989,13 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature,
 	free(keyid_to_free);
 	return ret;
 }
-
-/*
- * Strip CR from the line endings, in case we are on Windows.
- * NEEDSWORK: make it trim only CRs before LFs and rename
- */
-static void remove_cr_after(struct strbuf *buffer, size_t offset)
+/* Strip CR before LF from the line endings, in case we are on Windows. */
+static void strip_cr_before_lf(struct strbuf *buffer, size_t offset)
 {
 	size_t i, j;
 
 	for (i = j = offset; i < buffer->len; i++) {
-		if (buffer->buf[i] != '\r') {
+		if (buffer->buf[i] != '\r' || (i + 1 < buffer->len && buffer->buf[i + 1] != '\n')) {
 			if (i != j)
 				buffer->buf[j] = buffer->buf[i];
 			j++;
@@ -1049,8 +1045,8 @@ static int sign_buffer_gpg(struct strbuf *buffer, struct strbuf *signature,
 	}
 	strbuf_release(&gpg_status);
 
-	/* Strip CR from the line endings, in case we are on Windows. */
-	remove_cr_after(signature, bottom);
+	/* Strip CR before LF from the line endings, in case we are on Windows. */
+	strip_cr_before_lf(signature, bottom);
 
 	return 0;
 }
@@ -1136,8 +1132,8 @@ static int sign_buffer_ssh(struct strbuf *buffer, struct strbuf *signature,
 			ssh_signature_filename.buf);
 		goto out;
 	}
-	/* Strip CR from the line endings, in case we are on Windows. */
-	remove_cr_after(signature, bottom);
+	/* Strip CR before LF from the line endings, in case we are on Windows. */
+	strip_cr_before_lf(signature, bottom);
 
 out:
 	if (key_file)
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH/RFC 2/6] commit-reach: introduce struct paint_queue with per-side counters
From: Kristofer Karlsson @ 2026-06-23 10:13 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: Kristofer Karlsson via GitGitGadget, git, Elijah Newren
In-Reply-To: <8d07f5a9-82fa-4aed-b407-363e659f6851@gmail.com>

On Mon, 22 Jun 2026 at 22:23, Derrick Stolee <stolee@gmail.com> wrote:
>
> On 6/22/2026 3:14 PM, Kristofer Karlsson wrote:
> >
> > On Mon, 22 Jun 2026 at 20:10, Derrick Stolee <stolee@gmail.com> wrote:
> >>
> >> When possible, I like to try to make loops only have one terminating
> >> condition. Should we have paint_queue_get() return NULL when it sees
> >> this internal state condition?
> >
> > Possibly, but that would couple the paint_queue struct very tightly with
> > the usage. Not a problem in practice since it only has one call site, and
> > it's unlikely that we want to add more of them but it may feel more natural
> > to let the paint_queue purely have the queue semantics and counters,
> > and keep the halt condition within the function itself. I don't feel
> > super-strongly about this and can change it if needed, I will just need to
> > verify that nothing else gets complex as a result, I have not fully thought
> > through the effects.
>
> Hm. Interesting. The coupling is perhaps expected, because the data
> structure tracks counts that don't otherwise need to be tracked.
> Maybe the terminating condition method could be descriptively named
> to say why it would be completing.
>

I have been working on v2 locally and most of the changes landed
nicely and were clear improvements but there's one point I would
want to discuss a bit more.

For the termination conditions, I moved them into paint_queue_get()
as you suggested.  The all-zero check was straightforward since it
only depends on the counters but the side-exhaustion check also
needs to know whether we have entered the finite-generation region,
so I pass last_gen (already a local in paint_down_to_common) as a
parameter:

  static struct commit *paint_queue_get(struct paint_state *state,
                                        timestamp_t last_gen)

Inside, the two conditions merge nicely under a shared guard:

  if (!state->pending_merge_bases) {
      if (!state->p1_count && !state->p2_count)
          return NULL;
      if (last_gen < GENERATION_NUMBER_INFINITY &&
          (!state->p1_count || !state->p2_count))
          return NULL;
  }

Both conditions require pending_merge_bases == 0, so the nesting
felt natural. The first is "nothing non-stale left" (works in any
region). The second is "one side exhausted" (only in the finite
region where topological ordering holds).

I think passing in last_gen into paint_queue_get() feels _slightly_
awkward but not too bad in practice.  However, we also have my
older (first) patch with the fast-exit if the caller only needs one
merge base -- that has a separate break that also could be folded
into paint_queue_get(). The messy part here is that we would need
to also pass the mb_flags parameter to paint_queue_get().

Perhaps we should just let this remain as-is for now and follow up
with _removing_ that optimization. I think the value of having it
is much diminished (but not fully gone) by the side-exhaust approach.

Additionally there's a correctness argument to be made -- perhaps
all callers _should_ care about multiple merge bases existing, and
instead bail out if it finds more than one. The only use case
where this matters today is "git merge-base A B" without --all.

Right now I am leaning towards simply passing in last_gen and
containing all of the halt conditions there
(except the old !FIND_ALL).

The nicest alternative I can think of is to let this part only
break when the queue is empty:

  while ((commit = paint_queue_get(&state)))

and then adding a logical halt-section at the end of the while-loop
(where all the useful variables we need are already available), and
we could logically think of that as an optimization section, never
strictly needed for correctness.

> I just worry about the idea that a negative number (or an addition
> overflow) would create conditions for termination that we did not
> intend. That's why using the nonzero status as true/false combined
> with ands and ors is better.

Good point, I have addressed that locally too.

Thanks,
Kristofer

^ permalink raw reply

* Re: [PATCH v4 0/4] history: add squash subcommand to fold a range
From: Harald Nordgren @ 2026-06-23 10:41 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: Harald Nordgren via GitGitGadget, git
In-Reply-To: <ajkijomPo_kXSXul@pks.im>

Sorry about that.

I skipped the ancestry question because I felt the implementation
would get too complex and was hoping we could do without it. Probably
would have been better to be explicit about that instead of just
skipping it. I don't want to balloon the code more than necessary, but
I'll take a look at it now.


Harald

^ permalink raw reply

* Re: [PATCH v4] log: improve --follow following renames for non-linear history
From: Miklos Vajna @ 2026-06-23 11:30 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Jeff King, git
In-Reply-To: <xmqq1pdy4udg.fsf@gitster.g>

Hi Junio,

On Mon, Jun 22, 2026 at 05:44:43AM -0700, Junio C Hamano <gitster@pobox.com> wrote:
> went on in this patch would count), but as long as the resolution
> that is in my tree (as a part of 'seen') exactly matches what your
> update contains (meaning: rerere will do the same correct resolution
> when the topic gets merged to 'master' anyway) and the conflict is
> trivial to resolve by hand for others

I see, I'll keep that in mind for the future.

Thanks,

Miklos

^ permalink raw reply

* [GSoC Blog] Week 3&4 : Improve Disk Space Recovery for Partial Clones
From: Siddharth Shrimali @ 2026-06-23 11:46 UTC (permalink / raw)
  To: git; +Cc: Christian Couder, Siddharth Asthana, Siddharth Shrimali

Hello everyone,

My latest blog post, covering weeks 3 and 4, is now live:
https://siddharth.shrimali.info/#post/6

I have combined both weeks into a single post.
Why such a consolidation?
You’ll have to dive into the blog to know ;)

Please feel free to review my work and share your feedback.
Always open to discussions! :)

Regards,
Siddharth Shrimali

^ permalink raw reply

* Controle de votre adresse e-mail
From: Verification @ 2026-06-23 12:51 UTC (permalink / raw)
  To: git

Bonjour,

Ceci est un message automatique de controle d'adresse e-mail.
Aucune action n'est requise de votre part, vous pouvez l'ignorer.

Pour ne plus recevoir ce type de message, repondez STOP a cet e-mail.

^ permalink raw reply

* Re: [PATCH/RFC 3/6] commit-reach: terminate merge-base walk when one paint side is exhausted
From: Derrick Stolee @ 2026-06-23 13:40 UTC (permalink / raw)
  To: Kristofer Karlsson
  Cc: Kristofer Karlsson via GitGitGadget, git, Elijah Newren
In-Reply-To: <CAL71e4O7hKM=_M4K9hJE0MH9PdHUHxo7hyAbUSLbyk2wpiGxmw@mail.gmail.com>

On 6/22/2026 5:03 PM, Kristofer Karlsson wrote:
> On Mon, 22 Jun 2026 at 22:26, Derrick Stolee <stolee@gmail.com> wrote:
>>
>> I've used hyperfine [1] when doing specific performance tests
>> in the past. You can build Git before and after and have hyperfine
>> run the two modes and compare them:
>>
>>         hyperfine --warmup=3 \
>>                 -n 'old' "~/git-old/bin-wrappers/git -C $repo merge-base $A $B" \
>>                 -n 'new' "~/git-new/bin-wrappers/git -C $repo merge-base $A $B"
>>
>> [1] https://github.com/sharkdp/hyperfine
> 
> I can definitely use that, but I was thinking that the overhead
> of operations such as repo_parse_commit would be high relative
> to the overhead of the new paint_queue struct such that it would
> be hard to properly measure and that it would be easier if I could
> spread out that cost across multiple internal runs (which requires
> a custom binary of some sort), but perhaps it's enough to just
> show that there's no measurable regression here and then
> hyperfine is indeed the right fit. I'll start with that and see if I need
> to do anything more complex.
Unit-level performance is nice, but doesn't tell the whole story.

We typically focus on end-to-end performance numbers when possible.

Another way to do it would be to use trace2_region_enter() and
trace2_region_leave() markers and then pull the timing data out of
the trace2 event logs. It's more complicated and usually only
needed if we are struggling to reproduce the performance impact due
to external factors.

Thanks,
-Stolee


^ permalink raw reply

* Re: [GSoC] [Blog] week 4: Improving the new git repo command
From: K Jayatheerth @ 2026-06-23 13:41 UTC (permalink / raw)
  To: GIT Mailing-list, Justin Tobler, Lucas Seiki Oshiro
In-Reply-To: <CA+rGoLe+n314hrbKBSU61Hn=uVQN+OqOF5AVt2gPOityUUL_AA@mail.gmail.com>

Hi!

My Week 4 GSoC blog is live!
https://jayatheerth.com/blogs/gsoc/week-4-phase2

Feel free to give it a read and share any feedback ; )

Regards,
- K Jayatheerth

^ permalink raw reply

* Re: [PATCH v14 2/2] checkout: extend --track with a "fetch" mode to refresh start-point
From: Phillip Wood @ 2026-06-23 13:49 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget, git
  Cc: Ramsay Jones, D. Ben Knoble, Kristoffer Haugsbakk, Marc Branchaud,
	Harald Nordgren
In-Reply-To: <8518f090b1069a02d40c710975528ad118776b67.1781786652.git.gitgitgadget@gmail.com>

Hi Harald

On 18/06/2026 13:44, Harald Nordgren via GitGitGadget wrote:
> From: Harald Nordgren <haraldnordgren@gmail.com>
> 
> Add a "fetch" mode to the "--track" option of "git checkout" / "git
> switch" that refreshes <start-point> before checking it out:
> 
>      git checkout -b new_branch --track=fetch origin/some-branch
> 
> is shorthand for
> 
>      git fetch origin some-branch
>      git checkout -b new_branch --track origin/some-branch
> 
> Identify the remote whose configured fetch refspec maps to
> <start-point> using find_tracking_remote_for_ref() (the same lookup
> "--track" uses to pick which remote to record in
> branch.<name>.remote), then run "git fetch <remote> <src-ref>" for
> just that ref so other remote-tracking branches are left untouched.
> When <start-point> is a bare <remote> (e.g. "origin"), follow
> refs/remotes/<remote>/HEAD to learn which branch to refresh. If
> "git fetch" fails but the remote-tracking ref already exists locally,
> warn and proceed from the existing tip; otherwise abort.

This describes the feature well, but does not really explain why it is 
convenient to have a shorthand for "git fetch ... && git checkout -b 
...". For example if the reason is that in a fast-moving project you 
want to start your new work off the latest upstream changes to minimize 
the chance of merge conflicts or duplicated work it would be useful to 
say that. As Junio has said the implementation looks pretty solid I've 
left a few comments below, but the important thing to do first is to 
convince others that this is a useful feature and why it is worth 
blurring the separation between fetch and checkout. You can do that 
without sending a new version.

> diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc
> index a8b3b8c2e2..20b6cae60e 100644
> --- a/Documentation/git-checkout.adoc
> +++ b/Documentation/git-checkout.adoc
> @@ -158,11 +158,26 @@ of it").
>   	resets _<branch>_ to the start point instead of failing.
>   
>   `-t`::
> -`--track[=(direct|inherit)]`::
> +`--track[=(direct|inherit|fetch)[,...]]`::
>   	When creating a new branch, set up "upstream" configuration. See
>   	`--track` in linkgit:git-branch[1] for details. As a convenience,
>   	--track without -b implies branch creation.
>   +
> +The argument is a comma-separated list. `direct` (the default) and
> +`inherit` select the tracking mode and are mutually exclusive. Adding
> +`fetch` requests that the remote be fetched before _<start-point>_ is
> +resolved, so the new branch starts from a fresh tip: when
> +_<start-point>_ is in _<remote>/<branch>_ form, only that branch is
> +updated; when _<start-point>_ is a bare _<remote>_ (e.g. `origin`), the
> +branch named by _<remote>/HEAD_ is updated, and the checkout fails
> +with a hint to configure that symref if it is not set. The checkout
> +also fails if no configured remote's fetch refspec maps to
> +_<start-point>_, or if more than one does (in which case the `fetch`
> +cannot be unambiguously routed). If the fetch itself fails and the
> +corresponding remote-tracking ref already exists, a warning is printed
> +and the checkout proceeds from the existing tip; otherwise the checkout
> +is aborted.

Nicely explained

> +static void fetch_remote_for_start_point(const char *arg, int quiet)
> +{
> +	struct strbuf dst = STRBUF_INIT;
> +	struct tracking tracking;
> +	struct string_list tracking_srcs = STRING_LIST_INIT_DUP;
> +	struct string_list ambiguous_remotes = STRING_LIST_INIT_DUP;
> +	struct child_process cmd = CHILD_PROCESS_INIT;
> +	struct object_id oid;
> +	struct remote *named_remote;
> +	int bare_ns;
> +
> +	strbuf_addf(&dst, "refs/remotes/%s", arg);
> +	if (check_refname_format(dst.buf, 0))
> +		die(_("cannot fetch start-point '%s': not a valid "
> +		      "remote-tracking name"), arg);
> +
> +	named_remote = remote_get(arg);
> +	bare_ns = !strchr(arg, '/') ||
> +		(named_remote && remote_is_configured(named_remote, 1));
> +	if (bare_ns) {
> +		char *head_path = xstrfmt("refs/remotes/%s/HEAD", arg);
> +		const char *head_target =
> +			refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
> +						head_path,
> +						RESOLVE_REF_READING |
> +						RESOLVE_REF_NO_RECURSE,

Why do we use RESOLVE_REF_NO_RECURSE here? This should match whatever 
"git checkout -b <remote>" does.

> +						&oid, NULL);
> +		if (head_target &&
> +		    starts_with(head_target, dst.buf) &&
> +		    head_target[dst.len] == '/' &&
> +		    !check_refname_format(head_target, 0)) {

I don't think there is any need to call check_refname_format() here - 
you're using the result of reading a ref, not some untrusted input.

> +			strbuf_reset(&dst);
> +			strbuf_addstr(&dst, head_target);
> +			bare_ns = 0;
> +		}
> +		free(head_path);
> +	}
> +
> +	memset(&tracking, 0, sizeof(tracking));

When you want to zero initialize a stack variable it is easier, clearer 
and less error-prone to initialize it by adding "= {0};" where it is 
declared.

> +	tracking.spec.dst = dst.buf;
> +	tracking.srcs = &tracking_srcs;
> +	find_tracking_remote_for_ref(&tracking, &ambiguous_remotes);
> +
> +	if (tracking.matches > 1) {
> +		int status = die_message(_("cannot fetch start-point '%s': "
> +					   "fetch refspecs of multiple remotes "
> +					   "map to '%s'"), arg, dst.buf);
> +		advise_ambiguous_fetch_refspec(dst.buf, &ambiguous_remotes);
> +		exit(status);
> +	}
> +
> +	if (!tracking.matches) {
> +		if (bare_ns && named_remote &&
> +		    remote_is_configured(named_remote, 1))
> +			die(_("cannot fetch start-point '%s': "
> +			      "'refs/remotes/%s/HEAD' is not set; run "
> +			      "'git remote set-head %s --auto' to set it")

This is quite a long message for a single line - breaking the line and 
putting the suggested command on a separate line would make it clearer. 
Something like

cannot fetch start-point 'origin' because 'refs/remotes/origin/HEAD'
does not exist. To create it run

     git remote set-head origin --auto

> +			    arg, arg, arg);
> +		die(_("cannot fetch start-point '%s': no configured remote's "
> +		      "fetch refspec matches it"), arg);
> +	}
> +
> +	strvec_push(&cmd.args, "fetch");
> +	if (quiet)
> +		strvec_push(&cmd.args, "--quiet");
> +	strvec_pushl(&cmd.args, tracking.remote,
> +		     tracking_srcs.items[0].string, NULL);
> +	cmd.git_cmd = 1;
> +	if (run_command(&cmd)) {
> +		if (!refs_read_ref(get_main_ref_store(the_repository),
> +				   dst.buf, &oid))

You can use refs_ref_exists() to check a ref exists which avoids 
declaring "oid" which we're not interested in here.

> +			warning(_("failed to fetch start-point '%s'; "
> +				  "using existing '%s'"), arg, dst.buf);
> +		else
> +			die(_("failed to fetch start-point '%s'"), arg);
> +	}
> +
> +	string_list_clear(&tracking_srcs, 0);
> +	string_list_clear(&ambiguous_remotes, 0);
> +	strbuf_release(&dst);
> +}
> +
> +static int parse_opt_checkout_track(const struct option *opt,
> +				    const char *arg, int unset)
> +{
> +	struct checkout_opts *opts = opt->value;
> +	struct string_list tokens = STRING_LIST_INIT_DUP;
> +	struct string_list_item *item;
> +	int saw_direct = 0;
> +	int ret = 0;
> +
> +	opts->fetch = 0;
> +	if (unset) {
> +		opts->track = BRANCH_TRACK_NEVER;
> +		return 0;
> +	}
> +	opts->track = BRANCH_TRACK_EXPLICIT;
> +	if (!arg)
> +		return 0;
> +
> +	string_list_split(&tokens, arg, ",", -1);
> +	for_each_string_list_item(item, &tokens) {
> +		if (!strcmp(item->string, "fetch"))
> +			opts->fetch = 1;
> +		else if (!strcmp(item->string, "direct"))
> +			saw_direct = 1;
> +		else if (!strcmp(item->string, "inherit"))
> +			opts->track = BRANCH_TRACK_INHERIT;
> +		else {
> +			ret = error(_("option `%s' expects \"%s\", \"%s\", "
> +				      "or \"%s\""),
> +				    "--track", "direct", "inherit", "fetch");
> +			goto out;
> +		}
> +	}
> +	if (saw_direct && opts->track == BRANCH_TRACK_INHERIT)
> +		ret = error(_("option `%s' cannot combine \"%s\" and \"%s\""),
> +			    "--track", "direct", "inherit");

This parsing looks good
> diff --git a/t/t7201-co.sh b/t/t7201-co.sh
> index 7613b1d2a4..1e321b1512 100755
> --- a/t/t7201-co.sh
> +++ b/t/t7201-co.sh
> @@ -870,4 +870,280 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
>   	test_cmp_config "" --default "" branch.main2.merge
>   '

I've not read the tests in detail but there seem to be an awful lot of 
them. We only need to test each thing once so for example if we test

     git checkout --track=fetch -b <remote-ref>

with a fetch refspec, that maps refs/heads/*:refs/remotes/origin/xxx/* 
then we don't need to test it without that refspec. I notice you use 
"namespace" below with is confusing because it is not referring to the 
feature described in the gitnamespaces(7) man page.

Try and avoid

     test $a = $b

as it makes it hard to debug failing tests. Instead I think you can use 
test_cmp_rev in this case.

Thanks

Phillip

> +test_expect_success 'setup upstream for --track=fetch tests' '
> +	git checkout main &&
> +	git init fetch_upstream &&
> +	test_commit -C fetch_upstream u_main &&
> +	git remote add fetch_upstream fetch_upstream &&
> +	git fetch fetch_upstream &&
> +	git -C fetch_upstream checkout -b fetch_new &&
> +	test_commit -C fetch_upstream u_new
> +'
> +
> +test_expect_success 'checkout --track=fetch -b picks up branch created upstream after clone' '
> +	git checkout main &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_new &&
> +	git checkout --track=fetch -b local_new fetch_upstream/fetch_new &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_new HEAD &&
> +	test_cmp_config fetch_upstream branch.local_new.remote &&
> +	test_cmp_config refs/heads/fetch_new branch.local_new.merge
> +'
> +
> +test_expect_success 'checkout --track=fetch <remote>/<branch> leaves other tracking branches untouched' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_target &&
> +	test_commit -C fetch_upstream u_target_pre &&
> +	git -C fetch_upstream checkout -b fetch_other &&
> +	test_commit -C fetch_upstream u_other_pre &&
> +	git fetch fetch_upstream &&
> +	other_before=$(git rev-parse refs/remotes/fetch_upstream/fetch_other) &&
> +	git -C fetch_upstream checkout fetch_target &&
> +	test_commit -C fetch_upstream u_target_post &&
> +	git -C fetch_upstream checkout fetch_other &&
> +	test_commit -C fetch_upstream u_other_post &&
> +	git checkout --track=fetch -b local_target fetch_upstream/fetch_target &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_target HEAD &&
> +	test "$(git rev-parse refs/remotes/fetch_upstream/fetch_other)" = "$other_before"
> +'
> +
> +test_expect_success 'checkout --track=fetch with bare remote name fetches only <remote>/HEAD target' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout main &&
> +	git remote set-head fetch_upstream main &&
> +	git -C fetch_upstream checkout -b fetch_unrelated &&
> +	test_commit -C fetch_upstream u_unrelated_pre &&
> +	git fetch fetch_upstream fetch_unrelated &&
> +	unrelated_before=$(git rev-parse refs/remotes/fetch_upstream/fetch_unrelated) &&
> +	git -C fetch_upstream checkout main &&
> +	test_commit -C fetch_upstream u_main_post &&
> +	git -C fetch_upstream checkout fetch_unrelated &&
> +	test_commit -C fetch_upstream u_unrelated_post &&
> +	git checkout --track=fetch -b local_from_remote fetch_upstream &&
> +	test_cmp_rev refs/remotes/fetch_upstream/main HEAD &&
> +	test "$(git rev-parse refs/remotes/fetch_upstream/fetch_unrelated)" = "$unrelated_before"
> +'
> +
> +test_expect_success 'checkout --track=fetch aborts and does not create branch when no existing ref' '
> +	git checkout main &&
> +	test_might_fail git branch -D bogus &&
> +	test_must_fail git checkout --track=fetch -b bogus fetch_upstream/does_not_exist &&
> +	test_must_fail git rev-parse --verify refs/heads/bogus
> +'
> +
> +test_expect_success 'checkout --track=fetch warns and proceeds when fetch fails but ref exists' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_offline &&
> +	test_commit -C fetch_upstream u_offline &&
> +	git fetch fetch_upstream fetch_offline &&
> +	saved_url=$(git config remote.fetch_upstream.url) &&
> +	test_when_finished "git config remote.fetch_upstream.url \"$saved_url\"" &&
> +	git config remote.fetch_upstream.url ./does-not-exist &&
> +	git checkout --track=fetch -b local_offline fetch_upstream/fetch_offline 2>err &&
> +	test_grep "failed to fetch" err &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_offline HEAD
> +'
> +
> +test_expect_success 'checkout --track=fetch resolves through configured fetch refspec' '
> +	git checkout main &&
> +	git remote add fetch_custom ./fetch_upstream &&
> +	test_when_finished "git remote remove fetch_custom" &&
> +	git config --replace-all remote.fetch_custom.fetch \
> +		"+refs/heads/*:refs/remotes/custom-ns/*" &&
> +	git -C fetch_upstream checkout -b fetch_refspec &&
> +	test_commit -C fetch_upstream u_refspec &&
> +	test_must_fail git rev-parse --verify refs/remotes/custom-ns/fetch_refspec &&
> +	git checkout --track=fetch -b local_refspec custom-ns/fetch_refspec &&
> +	test_cmp_rev refs/remotes/custom-ns/fetch_refspec HEAD
> +'
> +
> +test_expect_success 'checkout --track=fetch on namespace bare name follows <ns>/HEAD' '
> +	git checkout main &&
> +	git remote add fetch_ns ./fetch_upstream &&
> +	test_when_finished "git remote remove fetch_ns" &&
> +	test_when_finished "git update-ref -d refs/remotes/ns_alias/HEAD" &&
> +	git config --replace-all remote.fetch_ns.fetch \
> +		"+refs/heads/*:refs/remotes/ns_alias/*" &&
> +	git fetch fetch_ns &&
> +	git symbolic-ref refs/remotes/ns_alias/HEAD refs/remotes/ns_alias/main &&
> +	git -C fetch_upstream checkout main &&
> +	test_commit -C fetch_upstream u_ns_post &&
> +	git checkout --track=fetch -b local_ns ns_alias &&
> +	test_cmp_rev refs/remotes/ns_alias/main HEAD &&
> +	test_cmp_config fetch_ns branch.local_ns.remote &&
> +	test_cmp_config refs/heads/main branch.local_ns.merge
> +'
> +
> +test_expect_success '--track=fetch on bare hierarchical remote name follows <ns>/HEAD' '
> +	git checkout main &&
> +	git remote add nested/bare ./fetch_upstream &&
> +	test_when_finished "git remote remove nested/bare" &&
> +	test_when_finished "git update-ref -d refs/remotes/nested/bare/HEAD" &&
> +	git fetch nested/bare &&
> +	git symbolic-ref refs/remotes/nested/bare/HEAD \
> +		refs/remotes/nested/bare/main &&
> +	git -C fetch_upstream checkout main &&
> +	test_commit -C fetch_upstream u_nested_bare_post &&
> +	git checkout --track=fetch -b local_nested_bare nested/bare &&
> +	test_cmp_rev refs/remotes/nested/bare/main HEAD
> +'
> +
> +test_expect_success 'checkout --track=fetch handles hierarchical remote name' '
> +	git checkout main &&
> +	git remote add nested/remote ./fetch_upstream &&
> +	test_when_finished "git remote remove nested/remote" &&
> +	git -C fetch_upstream checkout -b fetch_hier &&
> +	test_commit -C fetch_upstream u_hier &&
> +	test_must_fail git rev-parse --verify refs/remotes/nested/remote/fetch_hier &&
> +	git checkout --track=fetch -b local_hier nested/remote/fetch_hier &&
> +	test_cmp_rev refs/remotes/nested/remote/fetch_hier HEAD
> +'
> +
> +test_expect_success 'checkout --track=fetch dies on bare remote name with no <ns>/HEAD' '
> +	git checkout main &&
> +	git remote add fetch_nohead ./fetch_upstream &&
> +	test_when_finished "git remote remove fetch_nohead" &&
> +	test_might_fail git symbolic-ref -d refs/remotes/fetch_nohead/HEAD &&
> +	test_must_fail git checkout --track=fetch -b local_nohead fetch_nohead 2>err &&
> +	test_grep "refs/remotes/fetch_nohead/HEAD" err &&
> +	test_grep "git remote set-head fetch_nohead --auto" err &&
> +	test_must_fail git rev-parse --verify refs/heads/local_nohead
> +'
> +
> +test_expect_success 'checkout --track=fetch on bare unknown name does not suggest set-head' '
> +	git checkout main &&
> +	test_must_fail git rev-parse --verify refs/remotes/no_such_ns/HEAD &&
> +	test_must_fail git config --get remote.no_such_ns.url &&
> +	test_must_fail git checkout --track=fetch -b local_unknown no_such_ns 2>err &&
> +	test_grep "no configured remote" err &&
> +	test_grep ! "set-head" err &&
> +	test_must_fail git rev-parse --verify refs/heads/local_unknown
> +'
> +
> +test_expect_success 'checkout --track=fetch rejects <ns>/HEAD pointing outside namespace' '
> +	git checkout main &&
> +	git remote add fetch_crossns ./fetch_upstream &&
> +	test_when_finished "git remote remove fetch_crossns" &&
> +	test_when_finished "git update-ref -d refs/remotes/fetch_crossns/HEAD" &&
> +	git fetch fetch_crossns &&
> +	git symbolic-ref refs/remotes/fetch_crossns/HEAD \
> +		refs/remotes/fetch_upstream/u_main &&
> +	test_must_fail git checkout --track=fetch -b local_crossns fetch_crossns 2>err &&
> +	test_grep "refs/remotes/fetch_crossns/HEAD" err &&
> +	test_must_fail git rev-parse --verify refs/heads/local_crossns
> +'
> +
> +test_expect_success 'checkout --track=fetch dies on ambiguous fetch refspec match' '
> +	git checkout main &&
> +	git remote add fetch_ambig_a ./fetch_upstream &&
> +	git remote add fetch_ambig_b ./fetch_upstream &&
> +	test_when_finished "git remote remove fetch_ambig_a" &&
> +	test_when_finished "git remote remove fetch_ambig_b" &&
> +	git config --replace-all remote.fetch_ambig_a.fetch \
> +		"+refs/heads/*:refs/remotes/ambig_ns/*" &&
> +	git config --replace-all remote.fetch_ambig_b.fetch \
> +		"+refs/heads/*:refs/remotes/ambig_ns/*" &&
> +	git -C fetch_upstream checkout -b fetch_ambig &&
> +	test_commit -C fetch_upstream u_ambig &&
> +	test_must_fail git checkout --track=fetch -b local_ambig ambig_ns/fetch_ambig 2>err &&
> +	test_grep "fetch_ambig_a" err &&
> +	test_grep "fetch_ambig_b" err &&
> +	test_grep "tracking namespaces" err &&
> +	test_must_fail git rev-parse --verify refs/heads/local_ambig
> +'
> +
> +test_expect_success 'checkout --track=fetch rejects invalid refname components' '
> +	git checkout main &&
> +	test_must_fail git checkout --track=fetch -b local_invalid "foo..bar" 2>err &&
> +	test_grep "valid" err &&
> +	test_must_fail git rev-parse --verify refs/heads/local_invalid
> +'
> +
> +test_expect_success 'checkout --track=fetch,inherit rejects invalid refname components' '
> +	git checkout main &&
> +	test_must_fail git checkout --track=fetch,inherit -b local_invalid \
> +		"foo..bar" 2>err &&
> +	test_grep "valid" err &&
> +	test_must_fail git rev-parse --verify refs/heads/local_invalid
> +'
> +
> +test_expect_success 'checkout --track=inherit,direct is rejected' '
> +	test_must_fail git checkout --track=inherit,direct -b bad fetch_upstream/fetch_new 2>err &&
> +	test_grep "cannot combine" err
> +'
> +
> +test_expect_success 'checkout --track=direct,inherit is rejected' '
> +	test_must_fail git checkout --track=direct,inherit -b bad fetch_upstream/fetch_new 2>err &&
> +	test_grep "cannot combine" err
> +'
> +
> +test_expect_success 'checkout --track=fetch then --track=direct drops fetch (last-one-wins)' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_lastwin &&
> +	test_commit -C fetch_upstream u_lastwin &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_lastwin &&
> +	test_must_fail git checkout --track=fetch --track=direct \
> +		-b local_lastwin fetch_upstream/fetch_lastwin &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_lastwin
> +'
> +
> +test_expect_success 'checkout --track=fetch then --no-track drops fetch' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_notrack &&
> +	test_commit -C fetch_upstream u_notrack &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_notrack &&
> +	test_must_fail git checkout --track=fetch --no-track \
> +		-b local_notrack fetch_upstream/fetch_notrack &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_notrack
> +'
> +
> +test_expect_success 'checkout --track=fetch,inherit fetches remote-tracking start-point' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_inherit &&
> +	test_commit -C fetch_upstream u_inherit &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_inherit &&
> +	git checkout --track=fetch,inherit -b local_inherit \
> +		fetch_upstream/fetch_inherit &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_inherit HEAD
> +'
> +
> +test_expect_success 'checkout --track=fetch,inherit errors when start-point does not map to a remote' '
> +	git checkout main &&
> +	test_must_fail git checkout --track=fetch,inherit -b bad main 2>err &&
> +	test_grep "no configured remote" err &&
> +	test_must_fail git rev-parse --verify refs/heads/bad
> +'
> +
> +test_expect_success 'checkout --track=fetch on local start-point errors' '
> +	git checkout main &&
> +	test_must_fail git checkout --track=fetch -b bad main 2>err &&
> +	test_grep "no configured remote" err &&
> +	test_must_fail git rev-parse --verify refs/heads/bad
> +'
> +
> +test_expect_success 'checkout --track=bogus reports an error' '
> +	git checkout main &&
> +	test_must_fail git checkout --track=bogus -b bogus_branch fetch_upstream/fetch_new 2>err &&
> +	test_grep "expects" err
> +'
> +
> +test_expect_success 'checkout -q --track=fetch silences the fetch output' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_quiet &&
> +	test_commit -C fetch_upstream u_quiet &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_quiet &&
> +	git checkout -q --track=fetch -b local_quiet \
> +		fetch_upstream/fetch_quiet 2>err &&
> +	test_grep ! "-> fetch_upstream/fetch_quiet" err &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_quiet HEAD
> +'
> +
> +test_expect_success 'switch --track=fetch -c picks up branch created upstream after clone' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_switch &&
> +	test_commit -C fetch_upstream u_switch &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_switch &&
> +	git switch --track=fetch -c local_switch fetch_upstream/fetch_switch &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_switch HEAD
> +'
> +
>   test_done


^ permalink raw reply

* Re: [PATCH/RFC 2/6] commit-reach: introduce struct paint_queue with per-side counters
From: Derrick Stolee @ 2026-06-23 13:50 UTC (permalink / raw)
  To: Kristofer Karlsson
  Cc: Kristofer Karlsson via GitGitGadget, git, Elijah Newren
In-Reply-To: <CAL71e4NFHz_zVCWPvmTO8UPNyaKkDFqNQdd3CJykoiGmEhfUTA@mail.gmail.com>

On 6/23/2026 6:13 AM, Kristofer Karlsson wrote:
> On Mon, 22 Jun 2026 at 22:23, Derrick Stolee <stolee@gmail.com> wrote:
>>
>> On 6/22/2026 3:14 PM, Kristofer Karlsson wrote:
>>>
>>> On Mon, 22 Jun 2026 at 20:10, Derrick Stolee <stolee@gmail.com> wrote:
>>>>
>>>> When possible, I like to try to make loops only have one terminating
>>>> condition. Should we have paint_queue_get() return NULL when it sees
>>>> this internal state condition?
>>>
>>> Possibly, but that would couple the paint_queue struct very tightly with
>>> the usage. Not a problem in practice since it only has one call site, and
>>> it's unlikely that we want to add more of them but it may feel more natural
>>> to let the paint_queue purely have the queue semantics and counters,
>>> and keep the halt condition within the function itself. I don't feel
>>> super-strongly about this and can change it if needed, I will just need to
>>> verify that nothing else gets complex as a result, I have not fully thought
>>> through the effects.
>>
>> Hm. Interesting. The coupling is perhaps expected, because the data
>> structure tracks counts that don't otherwise need to be tracked.
>> Maybe the terminating condition method could be descriptively named
>> to say why it would be completing.
>>
> 
> I have been working on v2 locally and most of the changes landed
> nicely and were clear improvements but there's one point I would
> want to discuss a bit more.
> 
> For the termination conditions, I moved them into paint_queue_get()
> as you suggested.  The all-zero check was straightforward since it
> only depends on the counters but the side-exhaustion check also
> needs to know whether we have entered the finite-generation region,
> so I pass last_gen (already a local in paint_down_to_common) as a
> parameter:
> 
>   static struct commit *paint_queue_get(struct paint_state *state,
>                                         timestamp_t last_gen)
> 
> Inside, the two conditions merge nicely under a shared guard:
> 
>   if (!state->pending_merge_bases) {
>       if (!state->p1_count && !state->p2_count)
>           return NULL;
>       if (last_gen < GENERATION_NUMBER_INFINITY &&
>           (!state->p1_count || !state->p2_count))
>           return NULL;
>   }

This looks good to me. I'm not even bothered by the last_gen
parameter. You do make a good point about it being a potentially
leaky abstraction.

> Both conditions require pending_merge_bases == 0, so the nesting
> felt natural. The first is "nothing non-stale left" (works in any
> region). The second is "one side exhausted" (only in the finite
> region where topological ordering holds).
> 
> I think passing in last_gen into paint_queue_get() feels _slightly_
> awkward but not too bad in practice.  However, we also have my
> older (first) patch with the fast-exit if the caller only needs one
> merge base -- that has a separate break that also could be folded
> into paint_queue_get(). The messy part here is that we would need
> to also pass the mb_flags parameter to paint_queue_get().

How much of this data that you are passing into the method could be
state in the paint_queue struct? Could we have the paint_queue manage
all of the state necessary to make decisions around the walk
termination?

Or, could we do a peek into the queue to see the "top" commit, and
check if it is a finite commit or not? I know that 'last_gen' is
supposed to be the commit walked in the previous cycle, but it seems
that we only care about "the remaining commits are finite" as our
condition. 
> Perhaps we should just let this remain as-is for now and follow up
> with _removing_ that optimization. I think the value of having it
> is much diminished (but not fully gone) by the side-exhaust approach.
> 
> Additionally there's a correctness argument to be made -- perhaps
> all callers _should_ care about multiple merge bases existing, and
> instead bail out if it finds more than one. The only use case
> where this matters today is "git merge-base A B" without --all.

> Right now I am leaning towards simply passing in last_gen and
> containing all of the halt conditions there
> (except the old !FIND_ALL).

This is a good start, but hopefully storing the data in the
struct would be a good way to handle that.

Thanks,
-Stolee


^ permalink raw reply

* Re: [PATCH/RFC 2/6] commit-reach: introduce struct paint_queue with per-side counters
From: Kristofer Karlsson @ 2026-06-23 14:09 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: Kristofer Karlsson via GitGitGadget, git, Elijah Newren
In-Reply-To: <509fa950-fb9b-468d-b917-6c0eb7823d64@gmail.com>

On Tue, 23 Jun 2026 at 15:50, Derrick Stolee <stolee@gmail.com> wrote:
>
> > For the termination conditions, I moved them into paint_queue_get()
> > as you suggested.  The all-zero check was straightforward since it
> > only depends on the counters but the side-exhaustion check also
> > needs to know whether we have entered the finite-generation region,
> > so I pass last_gen (already a local in paint_down_to_common) as a
> > parameter:
> >
> >   static struct commit *paint_queue_get(struct paint_state *state,
> >                                         timestamp_t last_gen)
> >
> > Inside, the two conditions merge nicely under a shared guard:
> >
> >   if (!state->pending_merge_bases) {
> >       if (!state->p1_count && !state->p2_count)
> >           return NULL;
> >       if (last_gen < GENERATION_NUMBER_INFINITY &&
> >           (!state->p1_count || !state->p2_count))
> >           return NULL;
> >   }
>
> This looks good to me. I'm not even bothered by the last_gen
> parameter. You do make a good point about it being a potentially
> leaky abstraction.

Agreed, I am not also bothered by it.

> > Both conditions require pending_merge_bases == 0, so the nesting
> > felt natural. The first is "nothing non-stale left" (works in any
> > region). The second is "one side exhausted" (only in the finite
> > region where topological ordering holds).
> >
> > I think passing in last_gen into paint_queue_get() feels _slightly_
> > awkward but not too bad in practice.  However, we also have my
> > older (first) patch with the fast-exit if the caller only needs one
> > merge base -- that has a separate break that also could be folded
> > into paint_queue_get(). The messy part here is that we would need
> > to also pass the mb_flags parameter to paint_queue_get().
>
> How much of this data that you are passing into the method could be
> state in the paint_queue struct? Could we have the paint_queue manage
> all of the state necessary to make decisions around the walk
> termination?

Good idea, I think adding last_gen to the struct is doable and makes it cleaner.
If needed we could also add the mb_flags there (but would be a followup patch)
Minor note: I renamed the struct to paint_state so that I could rename
the prio_queue to queue and not have "queue.queue" which felt
confusing in the code.

> Or, could we do a peek into the queue to see the "top" commit, and
> check if it is a finite commit or not? I know that 'last_gen' is
> supposed to be the commit walked in the previous cycle, but it seems
> that we only care about "the remaining commits are finite" as our
> condition.

Yes, peeking into the queue would work too, but it would feel awkward,

  commit = prio_queue_peek();
  if (halt conditions) return NULL;
  prio_queue_get();

And if we get first, the condition is not valid - that said, it would be doable
to instead put the halt conditions _between_ popping the commit and
updating the counters. I am not sure how ugly or confusing it would be,
but I could add a comment to explain why that sequencing is important.
(Popping the commit and updating the counters may lead to temporary
0 counts, but then when we enqueue parents of the commits they
move away from the 0 anyway). It would become something like:

// dry-/pseudo-coded
  commit *paint_queue_pop() {
    commit = prio_queue_pop();
    if (!commit) return NULL;
    if (halt_condition(state, commit.generation)) return NULL;
    // important: don't decrement counters before checking the halt condition
    paint_count_update(state, commit->object.flags, -1);
    return commit;
  }

> > Right now I am leaning towards simply passing in last_gen and
> > containing all of the halt conditions there
> > (except the old !FIND_ALL).
>
> This is a good start, but hopefully storing the data in the
> struct would be a good way to handle that.

Sounds good, I will massage the code a bit, store the relevant pieces
in the struct
and see how clean I can make it.

Thanks,
Kristofer

^ 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