Git development
 help / color / mirror / Atom feed
* [PATCH v4 0/2] revision.c: implement --reverse=before for walks
From: Mirko Faina @ 2026-04-27  0:24 UTC (permalink / raw)
  To: git
  Cc: Mirko Faina, Junio C Hamano, Jeff King, Jean-Noël Avila,
	Patrick Steinhardt, Tian Yuchen, Ben Knoble
In-Reply-To: <cover.1776984666.git.mroik@delayed.space>

v4 aligns the behaviour for --reverse=before and --reverse=after on
negative numbers that are less than -1 to treat them the same as -1,
which is current behaviour (before this series).

[1/2] revision.c: implement --reverse=before for walks (Mirko Faina)
[2/2] revision.c: reduce memory usage on reverse before (Mirko Faina)

 Documentation/rev-list-options.adoc | 16 +++++--
 revision.c                          | 73 +++++++++++++++++++++++++++--
 revision.h                          |  8 +++-
 t/t4202-log.sh                      | 66 ++++++++++++++++++++++++++
 4 files changed, 153 insertions(+), 10 deletions(-)

Range-diff against v3:
1:  4864ac46dd = 1:  4864ac46dd revision.c: implement --reverse=before for walks
2:  00489b0e52 ! 2:  7c0bab5d14 revision.c: reduce memory usage on reverse before
    @@ Commit message
         revision with --reverse=before and --max-count=<k>. We do this through a
         simple queue. With N nodes and K as the --max-count argument, assuming K
         < N, we go from a space complexity of O(N) to O(K). When it comes down
    -    to time complexity, the queue has an ammortized time of O(1) for pops,
    -    so the complexity remains O(N).
    +    to time complexity, the queue has an amortized time of O(1) for pops, so
    +    the complexity remains O(N).
     
         Signed-off-by: Mirko Faina <mroik@delayed.space>
     
    @@ revision.c: struct commit *get_revision(struct rev_info *revs)
      		reversed = NULL;
     -		while ((c = get_revision_internal(revs)))
     -			commit_list_insert(c, &reversed);
    -+		if (revs->reverse == REVERSE_BEFORE && max_count != -1) {
    ++		if (revs->reverse == REVERSE_BEFORE && max_count >= 0) {
     +			retrieve_with_window(revs, max_count, &reversed);
     +		} else {
     +			while ((c = get_revision_internal(revs)))

base-commit: e8955061076952cc5eab0300424fc48b601fe12d
-- 
2.54.0


^ permalink raw reply

* Re: [PATCH] alias: restore support for simple dotted aliases
From: Jeff King @ 2026-04-26 23:01 UTC (permalink / raw)
  To: Jonatan Holmgren; +Cc: Junio C Hamano, git, rsch, michael.grossfeld
In-Reply-To: <4a130a23-fa32-460b-a338-409d85d18166@jontes.page>

On Sun, Apr 26, 2026 at 09:21:52PM +0200, Jonatan Holmgren wrote:

> I see the appeal of the overlapping-namespace approach for maximum compat.
> 
> My hesitation is that it introduces a config model where a single key
> (`alias.foo.bar`) no longer has a clear interpretation, but instead is
> implicitly treated as both a historical alias and structured data. That
> feels harder to reason about and document.
> 
> For the regression, I would lean toward a narrower compatibility rule:
> restore dotted aliases except where they collide with explicitly
> recognized structured keys (currently `command`). That keeps behavior
> predictable while still fixing the breakage.

Yeah, I agree with you here (and the rest of this email). My earlier
message was mostly about laying out the possible alternatives.

> One question: do we consider the historical dotted aliases something we
> want to preserve indefinitely, or just something to transition away from?
> My assumption has been that they were an accidental side-effect of the
> config parsing rather than a designed feature, but I agree they are now
> part of existing workflows and need to be handled carefully.

I think it would be OK to consider them a historical curiosity that may
eventually be removed, but without an active deprecation timeline. If
you do not need to work with older versions of Git it is already a good
idea to move to the new syntax because it prevents your alias being
caught up if further keys are added. It might be reasonable for the
documentation to note that (and of course also mention the downside,
which is that older versions of Git will not respect your alias).

We could eventually drop support, but I think it would have to be
either:

  1. In some distant version such that "pre-2.54" is considered ancient.

  2. At some version boundary where we declare a number of breaking
     changes. Git 3.0 is probably going to such a version, but I don't
     know if we have a concrete timeline (or how long we'd want a
     change to be in the "breaking changes" list in the build-up to that
     version).

There's a related question, too, about whether "alias.foo" (without
extra dots) could/should be dropped eventually. I don't see a particular
reason to do so, as the cost to carrying support is quite minimal.

-Peff

^ permalink raw reply

* Re: [PATCH] alias: restore support for simple dotted aliases
From: Jonatan Holmgren @ 2026-04-26 19:21 UTC (permalink / raw)
  To: Jeff King; +Cc: Junio C Hamano, git, rsch, michael.grossfeld
In-Reply-To: <20260425232916.GA29816@coredump.intra.peff.net>

I see the appeal of the overlapping-namespace approach for maximum compat.

My hesitation is that it introduces a config model where a single key
(`alias.foo.bar`) no longer has a clear interpretation, but instead is
implicitly treated as both a historical alias and structured data. That
feels harder to reason about and document.

For the regression, I would lean toward a narrower compatibility rule:
restore dotted aliases except where they collide with explicitly
recognized structured keys (currently `command`). That keeps behavior
predictable while still fixing the breakage.

If we want to grow metadata in the future, it might be better to make
that expansion explicit at that point rather than baking in ambiguity
now.

I think the worst outcome from this thread would be moving the
new alias syntax into a different namespace entirely (e.g., `nalias`).
If namespace cleanliness is the priority, reserving `command` and
`alias-*` still seems like the best path forward to me.

One question: do we consider the historical dotted aliases something we
want to preserve indefinitely, or just something to transition away from?
My assumption has been that they were an accidental side-effect of the
config parsing rather than a designed feature, but I agree they are now
part of existing workflows and need to be handled carefully.

^ permalink raw reply

* [PATCH] diff: simplify line-range filter by classifying removals immediately
From: Michael Montalbo via GitGitGadget @ 2026-04-26 19:10 UTC (permalink / raw)
  To: git; +Cc: Michael Montalbo, Michael Montalbo

From: Michael Montalbo <mmontalbo@gmail.com>

The line-range filter buffered '-' (removal) lines in a pending_rm
strbuf, deferring their classification until a '+' or ' ' line
arrived to reveal the post-image position.

This buffering is unnecessary.  Removal lines are pre-image content
that occupies no post-image space, so they do not advance lno_post.
Within a hunk, xdiff emits removals before additions for each
change, so a '-' line always arrives while lno_post is at the same
position that the following '+' or ' ' line will occupy.  Each
line can therefore be classified against the tracked ranges as it
arrives, without waiting for a non-removal line to confirm the
position.

The buffering also had a bug: flush_rhunk() unconditionally drained
the pending buffer when the range hunk was active, even if lno_post
had advanced past the tracked range.  This caused deletions
immediately after the tracked function to be incorrectly included
in patch output.

Remove the pending_rm buffer and classify '-' lines using the same
in_range check applied to '+' and ' ' lines.  This simplifies the
filter, removes three struct fields and a helper function, and
makes the flush_rhunk() bug impossible by construction.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
---
    diff: simplify line-range filter by classifying removals immediately
    
    The line-range filter buffered '-' (removal) lines in a
    pending_rm strbuf, deferring their classification until a '+'
    or ' ' line arrived to reveal the post-image position.
    
    This buffering is unnecessary. Removal lines are pre-image
    content that occupies no post-image space, so they do not
    advance lno_post. Within a hunk, xdiff emits removals before additions
    for each change, so a '-' line always arrives while
    lno_post is at the same position that the following '+' or ' ' line will
    occupy. Each line can therefore be classified against
    the tracked ranges as it arrives, without waiting for a
    non-removal line to confirm the position.
    
    The buffering also had a bug: flush_rhunk() unconditionally
    drained the pending buffer when the range hunk was active, even
    if lno_post had advanced past the tracked range. This caused deletions
    immediately after the tracked function to be
    incorrectly included in patch output.
    
    Remove the pending_rm buffer and classify '-' lines using the
    same in_range check applied to '+' and ' ' lines. This simplifies the
    filter, removes three struct fields and a helper function, and makes the
    flush_rhunk() bug impossible by
    construction.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2099%2Fmmontalbo%2Fmm%2Fline-log-immediate-classify-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2099/mmontalbo/mm/line-log-immediate-classify-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2099

 diff.c              | 110 ++++++++++++---------------------------
 t/t4211-line-log.sh | 124 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+), 77 deletions(-)

diff --git a/diff.c b/diff.c
index 397e38b41c..5662d080be 100644
--- a/diff.c
+++ b/diff.c
@@ -616,11 +616,6 @@ struct emit_callback {
  * the requested ranges.  Contiguous in-range lines are collected into
  * range hunks and flushed with a synthetic @@ header so that
  * fn_out_consume() sees well-formed unified-diff fragments.
- *
- * Removal lines ('-') cannot be classified by post-image position, so
- * they are buffered in pending_rm until the next '+' or ' ' line
- * reveals whether they precede an in-range line (flush into range hunk) or
- * an out-of-range line (discard).
  */
 struct line_range_callback {
 	xdiff_emit_line_fn orig_line_fn;
@@ -646,11 +641,6 @@ struct line_range_callback {
 	int rhunk_active;
 	int rhunk_has_changes;		/* any '+' or '-' lines? */
 
-	/* Removal lines not yet known to be in-range */
-	struct strbuf pending_rm;
-	int pending_rm_count;
-	long pending_rm_pre_begin;	/* pre-image line of first pending */
-
 	int ret;			/* latched error from orig_line_fn */
 };
 
@@ -2539,12 +2529,6 @@ static int quick_consume(void *priv, char *line UNUSED, unsigned long len UNUSED
 	return 1;
 }
 
-static void discard_pending_rm(struct line_range_callback *s)
-{
-	strbuf_reset(&s->pending_rm);
-	s->pending_rm_count = 0;
-}
-
 static void flush_rhunk(struct line_range_callback *s)
 {
 	struct strbuf hdr = STRBUF_INIT;
@@ -2553,14 +2537,6 @@ static void flush_rhunk(struct line_range_callback *s)
 	if (!s->rhunk_active || s->ret)
 		return;
 
-	/* Drain any pending removal lines into the range hunk */
-	if (s->pending_rm_count) {
-		strbuf_addbuf(&s->rhunk, &s->pending_rm);
-		s->rhunk_old_count += s->pending_rm_count;
-		s->rhunk_has_changes = 1;
-		discard_pending_rm(s);
-	}
-
 	/*
 	 * Suppress context-only hunks: they contain no actual changes
 	 * and would just be noise.  This can happen when the inflated
@@ -2615,11 +2591,6 @@ static void line_range_hunk_fn(void *data,
 	 * When count > 0, begin is 1-based.  When count == 0, begin is
 	 * adjusted down by 1 by xdl_emit_hunk_hdr(), but no lines of
 	 * that type will arrive, so the value is unused.
-	 *
-	 * Any pending removal lines from the previous xdiff hunk are
-	 * intentionally left in pending_rm: the line callback will
-	 * flush or discard them when the next content line reveals
-	 * whether the removals precede in-range content.
 	 */
 	s->lno_post = new_begin;
 	s->lno_pre = old_begin;
@@ -2635,88 +2606,75 @@ static void line_range_hunk_fn(void *data,
 static int line_range_line_fn(void *priv, char *line, unsigned long len)
 {
 	struct line_range_callback *s = priv;
-	const struct range *cur;
-	long lno_0, cur_pre;
+	long lno_0;
+	int in_range;
 
 	if (s->ret)
 		return s->ret;
 
-	if (line[0] == '-') {
-		if (!s->pending_rm_count)
-			s->pending_rm_pre_begin = s->lno_pre;
-		s->lno_pre++;
-		strbuf_add(&s->pending_rm, line, len);
-		s->pending_rm_count++;
-		return s->ret;
-	}
-
 	if (line[0] == '\\') {
-		if (s->pending_rm_count)
-			strbuf_add(&s->pending_rm, line, len);
-		else if (s->rhunk_active)
+		if (s->rhunk_active)
 			strbuf_add(&s->rhunk, line, len);
-		/* otherwise outside tracked range; drop silently */
 		return s->ret;
 	}
 
-	if (line[0] != '+' && line[0] != ' ')
+	if (line[0] != '+' && line[0] != ' ' && line[0] != '-')
 		BUG("unexpected diff line type '%c'", line[0]);
 
+	/*
+	 * Compute post-image position.  '+' and ' ' lines advance
+	 * lno_post; '-' lines do not (they occupy no post-image space).
+	 */
 	lno_0 = s->lno_post - 1;
-	cur_pre = s->lno_pre;	/* save before advancing for context lines */
-	s->lno_post++;
-	if (line[0] == ' ')
-		s->lno_pre++;
+	if (line[0] != '-')
+		s->lno_post++;
 
-	/* Advance past ranges we've passed */
+	/*
+	 * Advance past any ranges we've moved beyond.  Emit the
+	 * accumulated range hunk for the range we're leaving.
+	 */
 	while (s->cur_range < s->ranges->nr &&
 	       lno_0 >= s->ranges->ranges[s->cur_range].end) {
 		if (s->rhunk_active)
 			flush_rhunk(s);
-		discard_pending_rm(s);
 		s->cur_range++;
 	}
 
-	/* Past all ranges */
-	if (s->cur_range >= s->ranges->nr) {
-		discard_pending_rm(s);
-		return s->ret;
-	}
-
-	cur = &s->ranges->ranges[s->cur_range];
+	in_range = s->cur_range < s->ranges->nr &&
+		   lno_0 >= s->ranges->ranges[s->cur_range].start &&
+		   lno_0 < s->ranges->ranges[s->cur_range].end;
 
-	/* Before current range */
-	if (lno_0 < cur->start) {
-		discard_pending_rm(s);
+	if (!in_range) {
+		if (line[0] != '+')
+			s->lno_pre++;
 		return s->ret;
 	}
 
-	/* In range so start a new range hunk if needed */
+	/* Start a new range hunk if this is the first in-range line */
 	if (!s->rhunk_active) {
 		s->rhunk_active = 1;
 		s->rhunk_has_changes = 0;
 		s->rhunk_new_begin = lno_0 + 1;
-		s->rhunk_old_begin = s->pending_rm_count
-			? s->pending_rm_pre_begin : cur_pre;
+		s->rhunk_old_begin = s->lno_pre;
 		s->rhunk_old_count = 0;
 		s->rhunk_new_count = 0;
 		strbuf_reset(&s->rhunk);
 	}
 
-	/* Flush pending removals into range hunk */
-	if (s->pending_rm_count) {
-		strbuf_addbuf(&s->rhunk, &s->pending_rm);
-		s->rhunk_old_count += s->pending_rm_count;
-		s->rhunk_has_changes = 1;
-		discard_pending_rm(s);
-	}
-
+	/* Append line to the range hunk */
 	strbuf_add(&s->rhunk, line, len);
-	s->rhunk_new_count++;
-	if (line[0] == '+')
+	if (line[0] == '-') {
+		s->rhunk_old_count++;
 		s->rhunk_has_changes = 1;
-	else
+		s->lno_pre++;
+	} else if (line[0] == '+') {
+		s->rhunk_new_count++;
+		s->rhunk_has_changes = 1;
+	} else {
 		s->rhunk_old_count++;
+		s->rhunk_new_count++;
+		s->lno_pre++;
+	}
 
 	return s->ret;
 }
@@ -4072,7 +4030,6 @@ static void builtin_diff(const char *name_a,
 			lr_state.orig_cb_data = &ecbdata;
 			lr_state.ranges = line_ranges;
 			strbuf_init(&lr_state.rhunk, 0);
-			strbuf_init(&lr_state.pending_rm, 0);
 
 			/*
 			 * Inflate ctxlen so that all changes within
@@ -4107,7 +4064,6 @@ static void builtin_diff(const char *name_a,
 				die("unable to generate diff for %s",
 				    one->path);
 			strbuf_release(&lr_state.rhunk);
-			strbuf_release(&lr_state.pending_rm);
 		} else if (xdi_diff_outf(&mf1, &mf2, NULL, fn_out_consume,
 					 &ecbdata, &xpp, &xecfg))
 			die("unable to generate diff for %s", one->path);
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index aaf197d2ed..2bff0e4c26 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -711,4 +711,128 @@ test_expect_success '-L with -G filters to diff-text matches' '
 	grep "F2 + 2" actual
 '
 
+test_expect_success 'setup for trailing deletion test' '
+	git checkout --orphan trailing-del &&
+	git reset --hard &&
+	cat >file.c <<-\EOF &&
+	void tracked()
+	{
+	    return 1;
+	}
+	// trailing comment
+	EOF
+	git add file.c &&
+	test_tick &&
+	git commit -m "add file with trailing comment" &&
+	# Modify tracked() AND delete the trailing comment in
+	# one commit, so the commit touches the tracked range
+	# and is not filtered out by the revision walker.
+	cat >file.c <<-\EOF &&
+	void tracked()
+	{
+	    return 2;
+	}
+	EOF
+	git commit -a -m "modify tracked and delete trailing comment"
+'
+
+test_expect_success '-L does not include deletions past end of tracked range' '
+	git log -L:tracked:file.c --format= -1 -p >actual &&
+	# The trailing comment deletion is outside the tracked
+	# range and should not appear in the patch output.
+	grep "return 2" actual &&
+	! grep "trailing comment" actual
+'
+
+test_expect_success '-L includes leading deletions resolved by in-range line' '
+	git checkout --orphan leading-del &&
+	git reset --hard &&
+	cat >file.c <<-\EOF &&
+	// leading comment
+	void tracked()
+	{
+	    return 1;
+	}
+	EOF
+	git add file.c &&
+	test_tick &&
+	git commit -m "add file with leading comment" &&
+	cat >file.c <<-\EOF &&
+	void tracked()
+	{
+	    return 2;
+	}
+	EOF
+	git commit -a -m "modify tracked and delete leading comment" &&
+	git log -L:tracked:file.c --format= -1 -p >actual &&
+	# The leading comment deletion is resolved by the next
+	# non-removal line (void tracked), which is in range.
+	# Pending removals are attributed to the range they precede.
+	grep "return 2" actual &&
+	grep "leading comment" actual
+'
+
+test_expect_success 'setup for line-range filter edge cases' '
+	git checkout --orphan filter-edge &&
+	git reset --hard &&
+	cat >file.c <<-\EOF &&
+	void before()
+	{
+	    return 0;
+	}
+
+	void tracked()
+	{
+	    int a = 1;
+	    int b = 2;
+	    int c = 3;
+	    return a + b + c;
+	}
+
+	void after()
+	{
+	    return 9;
+	}
+	EOF
+	git add file.c &&
+	test_tick &&
+	git commit -m "initial"
+'
+
+test_expect_success '-L change at exact first line of range' '
+	git checkout filter-edge &&
+	# Change the function signature (first line of range)
+	sed "s/void tracked/int tracked/" file.c >tmp &&
+	mv tmp file.c &&
+	git commit -a -m "change first line" &&
+	git log -L:tracked:file.c -p --format=%s -1 >actual &&
+	grep "change first line" actual &&
+	grep "+int tracked" actual &&
+	grep "\\-void tracked" actual
+'
+
+test_expect_success '-L change at exact last line of range' '
+	git checkout filter-edge &&
+	git reset --hard HEAD~1 &&
+	# Change the closing brace line (last line of range)
+	sed "s/^}$/} \/\/ end tracked/" file.c >tmp &&
+	mv tmp file.c &&
+	git commit -a -m "change last line" &&
+	git log -L:tracked:file.c -p --format=%s -1 >actual &&
+	grep "change last line" actual &&
+	grep "end tracked" actual
+'
+
+test_expect_success '-L pure deletion in range (no additions)' '
+	git checkout filter-edge &&
+	git reset --hard HEAD~1 &&
+	# Delete a line inside tracked() without adding anything
+	sed "/int c/d" file.c >tmp &&
+	mv tmp file.c &&
+	git commit -a -m "pure deletion" &&
+	git log -L:tracked:file.c -p --format=%s -1 >actual &&
+	grep "pure deletion" actual &&
+	grep "\\-.*int c" actual
+'
+
 test_done

base-commit: 9f223ef1c026d91c7ac68cc0211bde255dda6199
-- 
gitgitgadget

^ permalink raw reply related

* [PATCH v4] checkout: add --fetch to fetch remote before resolving start-point
From: Harald Nordgren via GitGitGadget @ 2026-04-26 18:32 UTC (permalink / raw)
  To: git
  Cc: Ramsay Jones, D. Ben Knoble, Kristoffer Haugsbakk, Marc Branchaud,
	Harald Nordgren, Harald Nordgren
In-Reply-To: <pull.2281.v3.git.git.1777188295021.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

A common workflow is:

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

The first command exists purely so the second sees an up-to-date view
of the remote. If it is forgotten, origin/some-branch points at a stale
commit and the new local branch is created from the wrong start point.

Teach checkout (and switch) a --fetch flag that folds the two steps
into one:

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

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

Also add a checkout.fetch config to enable this by default.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
    checkout: add --fetch to fetch remote before resolving start-point
    
    Adding tests to confirm that '--no-fetch' can countermand the config.

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

Range-diff vs v3:

 1:  df7b63862c ! 1:  150ccbb621 checkout: add --fetch to fetch remote before resolving start-point
     @@ t/t7201-co.sh: test_expect_success 'tracking info copied with autoSetupMerge=inh
      +	test_cmp_rev refs/remotes/fetch_upstream/fetch_cfg HEAD
      +'
      +
     ++test_expect_success '--no-fetch overrides checkout.fetch=true' '
     ++	git checkout main &&
     ++	git -C fetch_upstream checkout -b fetch_nofetch &&
     ++	test_commit -C fetch_upstream u_nofetch &&
     ++	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_nofetch &&
     ++	test_must_fail git -c checkout.fetch=true checkout --no-fetch \
     ++		-b local_nofetch fetch_upstream/fetch_nofetch &&
     ++	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_nofetch &&
     ++	test_must_fail git rev-parse --verify refs/heads/local_nofetch
     ++'
     ++
     ++test_expect_success '--no-fetch overrides earlier --fetch on command line' '
     ++	git checkout main &&
     ++	git -C fetch_upstream checkout -b fetch_override &&
     ++	test_commit -C fetch_upstream u_override &&
     ++	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_override &&
     ++	test_must_fail git checkout --fetch --no-fetch \
     ++		-b local_override fetch_upstream/fetch_override &&
     ++	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_override &&
     ++	test_must_fail git rev-parse --verify refs/heads/local_override
     ++'
     ++
      +test_expect_success 'switch --fetch -c picks up branch created upstream after clone' '
      +	git checkout main &&
      +	git -C fetch_upstream checkout -b fetch_switch &&


 Documentation/config/checkout.adoc |  5 ++
 Documentation/git-checkout.adoc    | 11 ++++
 Documentation/git-switch.adoc      | 11 ++++
 builtin/checkout.c                 | 50 ++++++++++++++++-
 t/t7201-co.sh                      | 90 ++++++++++++++++++++++++++++++
 t/t9902-completion.sh              |  1 +
 6 files changed, 166 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/checkout.adoc b/Documentation/config/checkout.adoc
index e35d212969..c95f72b38e 100644
--- a/Documentation/config/checkout.adoc
+++ b/Documentation/config/checkout.adoc
@@ -22,6 +22,11 @@ commands or functionality in the future.
 	option in `git checkout` and `git switch`. See
 	linkgit:git-switch[1] and linkgit:git-checkout[1].
 
+`checkout.fetch`::
+	Provides the default value for the `--fetch` or `--no-fetch`
+	option in `git checkout` and `git switch`. See
+	linkgit:git-switch[1] and linkgit:git-checkout[1].
+
 `checkout.workers`::
 	The number of parallel workers to use when updating the working tree.
 	The default is one, i.e. sequential execution. If set to a value less
diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc
index 43ccf47cf6..f5cc1ced74 100644
--- a/Documentation/git-checkout.adoc
+++ b/Documentation/git-checkout.adoc
@@ -201,6 +201,17 @@ linkgit:git-config[1].
 The default behavior can be set via the `checkout.guess` configuration
 variable.
 
+`--fetch`::
+`--no-fetch`::
+	If _<start-point>_ refers to a remote-tracking branch, fetch
+	from that remote before resolving it. When _<start-point>_ is
+	in _<remote>/<branch>_ form, only that branch is updated; when
+	it is a bare remote name (e.g. `origin`), the whole remote is
+	fetched. If the fetch fails, the checkout is aborted.
++
+The default behavior can be set via the `checkout.fetch` configuration
+variable.
+
 `-l`::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
diff --git a/Documentation/git-switch.adoc b/Documentation/git-switch.adoc
index 87707e9265..29743bafea 100644
--- a/Documentation/git-switch.adoc
+++ b/Documentation/git-switch.adoc
@@ -110,6 +110,17 @@ ambiguous but exists on the 'origin' remote. See also
 The default behavior can be set via the `checkout.guess` configuration
 variable.
 
+`--fetch`::
+`--no-fetch`::
+	If _<start-point>_ refers to a remote-tracking branch, fetch
+	from that remote before resolving it. When _<start-point>_ is
+	in _<remote>/<branch>_ form, only that branch is updated; when
+	it is a bare remote name (e.g. `origin`), the whole remote is
+	fetched. If the fetch fails, the switch is aborted.
++
+The default behavior can be set via the `checkout.fetch` configuration
+variable.
+
 `-f`::
 `--force`::
 	An alias for `--discard-changes`.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index e031e61886..8d810fe2fa 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -30,7 +30,9 @@
 #include "repo-settings.h"
 #include "resolve-undo.h"
 #include "revision.h"
+#include "run-command.h"
 #include "setup.h"
+#include "strvec.h"
 #include "submodule.h"
 #include "symlinks.h"
 #include "trace2.h"
@@ -61,6 +63,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int dwim_new_local_branch;
+	int fetch;
 	int discard_changes;
 	int accept_ref;
 	int accept_pathspec;
@@ -112,6 +115,36 @@ struct branch_info {
 	char *checkout;
 };
 
+static void fetch_remote_for_start_point(const char *arg)
+{
+	const char *slash;
+	char *remote_name;
+	struct remote *remote;
+	struct child_process cmd = CHILD_PROCESS_INIT;
+
+	if (!arg || !*arg)
+		return;
+
+	slash = strchr(arg, '/');
+	if (slash == arg)
+		return;
+	remote_name = slash ? xstrndup(arg, slash - arg) : xstrdup(arg);
+
+	remote = remote_get(remote_name);
+	if (!remote || !remote_is_configured(remote, 1)) {
+		free(remote_name);
+		return;
+	}
+
+	strvec_pushl(&cmd.args, "fetch", remote_name, NULL);
+	if (slash && slash[1])
+		strvec_push(&cmd.args, slash + 1);
+	cmd.git_cmd = 1;
+	free(remote_name);
+	if (run_command(&cmd))
+		die(_("failed to fetch start-point '%s'"), arg);
+}
+
 static void branch_info_release(struct branch_info *info)
 {
 	free(info->name);
@@ -1237,6 +1270,10 @@ static int git_checkout_config(const char *var, const char *value,
 		opts->dwim_new_local_branch = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "checkout.fetch")) {
+		opts->fetch = git_config_bool(var, value);
+		return 0;
+	}
 
 	if (starts_with(var, "submodule."))
 		return git_default_submodule_config(var, value, NULL);
@@ -1942,8 +1979,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
-		int n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
-					     &new_branch_info, opts, &rev);
+		int n;
+
+		if (opts->fetch)
+			fetch_remote_for_start_point(argv[0]);
+
+		n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
+					 &new_branch_info, opts, &rev);
 		argv += n;
 		argc -= n;
 	} else if (!opts->accept_ref && opts->from_treeish) {
@@ -2052,6 +2094,8 @@ int cmd_checkout(int argc,
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
 		OPT_BOOL(0, "auto-advance", &opts.auto_advance,
 			 N_("auto advance to the next file when selecting hunks interactively")),
+		OPT_BOOL(0, "fetch", &opts.fetch,
+			 N_("fetch from the remote first if <start-point> is a remote-tracking branch")),
 		OPT_END()
 	};
 
@@ -2102,6 +2146,8 @@ int cmd_switch(int argc,
 			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
 			 N_("throw away local modifications")),
+		OPT_BOOL(0, "fetch", &opts.fetch,
+			 N_("fetch from the remote first if <start-point> is a remote-tracking branch")),
 		OPT_END()
 	};
 
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 9bcf7c0b40..731be2680a 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -801,4 +801,94 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
 	test_cmp_config "" --default "" branch.main2.merge
 '
 
+test_expect_success 'setup upstream for --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 --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 --fetch -b local_new fetch_upstream/fetch_new &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_new HEAD
+'
+
+test_expect_success 'checkout --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 --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 --fetch with bare remote name fetches the remote' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_new2 &&
+	test_commit -C fetch_upstream u_new2 &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2 &&
+	git checkout --fetch -b local_from_remote fetch_upstream &&
+	git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2
+'
+
+test_expect_success 'checkout --fetch aborts and does not create branch on fetch failure' '
+	git checkout main &&
+	test_might_fail git branch -D bogus &&
+	test_must_fail git checkout --fetch -b bogus fetch_upstream/does_not_exist &&
+	test_must_fail git rev-parse --verify refs/heads/bogus
+'
+
+test_expect_success 'checkout.fetch=true enables fetching without --fetch' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_cfg &&
+	test_commit -C fetch_upstream u_cfg &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_cfg &&
+	git -c checkout.fetch=true checkout -b local_cfg fetch_upstream/fetch_cfg &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_cfg HEAD
+'
+
+test_expect_success '--no-fetch overrides checkout.fetch=true' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_nofetch &&
+	test_commit -C fetch_upstream u_nofetch &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_nofetch &&
+	test_must_fail git -c checkout.fetch=true checkout --no-fetch \
+		-b local_nofetch fetch_upstream/fetch_nofetch &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_nofetch &&
+	test_must_fail git rev-parse --verify refs/heads/local_nofetch
+'
+
+test_expect_success '--no-fetch overrides earlier --fetch on command line' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_override &&
+	test_commit -C fetch_upstream u_override &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_override &&
+	test_must_fail git checkout --fetch --no-fetch \
+		-b local_override fetch_upstream/fetch_override &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_override &&
+	test_must_fail git rev-parse --verify refs/heads/local_override
+'
+
+test_expect_success 'switch --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 --fetch -c local_switch fetch_upstream/fetch_switch &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_switch HEAD
+'
+
 test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..dc1d63669f 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2602,6 +2602,7 @@ test_expect_success 'double dash "git checkout"' '
 	--ignore-other-worktrees Z
 	--recurse-submodules Z
 	--auto-advance Z
+	--fetch Z
 	--progress Z
 	--guess Z
 	--no-guess Z

base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
-- 
gitgitgadget

^ permalink raw reply related

* Re: [PATCH v3] checkout: add --fetch to fetch remote before resolving start-point
From: Ramsay Jones @ 2026-04-26 15:54 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget, git
  Cc: D. Ben Knoble, Kristoffer Haugsbakk, Marc Branchaud,
	Harald Nordgren
In-Reply-To: <pull.2281.v3.git.git.1777188295021.gitgitgadget@gmail.com>



On 26/04/2026 8:24 am, Harald Nordgren via GitGitGadget wrote:
> From: Harald Nordgren <haraldnordgren@gmail.com>
> 
[snip]

> diff --git a/t/t7201-co.sh b/t/t7201-co.sh
> index 9bcf7c0b40..cf2ceb4052 100755
> --- a/t/t7201-co.sh
> +++ b/t/t7201-co.sh
> @@ -801,4 +801,72 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
>  	test_cmp_config "" --default "" branch.main2.merge
>  '
>  
> +test_expect_success 'setup upstream for --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 --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 --fetch -b local_new fetch_upstream/fetch_new &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_new HEAD
> +'
> +
> +test_expect_success 'checkout --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 --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 --fetch with bare remote name fetches the remote' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_new2 &&
> +	test_commit -C fetch_upstream u_new2 &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2 &&
> +	git checkout --fetch -b local_from_remote fetch_upstream &&
> +	git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2
> +'
> +
> +test_expect_success 'checkout --fetch aborts and does not create branch on fetch failure' '
> +	git checkout main &&
> +	test_might_fail git branch -D bogus &&
> +	test_must_fail git checkout --fetch -b bogus fetch_upstream/does_not_exist &&
> +	test_must_fail git rev-parse --verify refs/heads/bogus
> +'
> +
> +test_expect_success 'checkout.fetch=true enables fetching without --fetch' '
> +	git checkout main &&
> +	git -C fetch_upstream checkout -b fetch_cfg &&
> +	test_commit -C fetch_upstream u_cfg &&
> +	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_cfg &&
> +	git -c checkout.fetch=true checkout -b local_cfg fetch_upstream/fetch_cfg &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_cfg HEAD
> +'
> +
> +test_expect_success 'switch --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 --fetch -c local_switch fetch_upstream/fetch_switch &&
> +	test_cmp_rev refs/remotes/fetch_upstream/fetch_switch HEAD
> +'
> +
>  test_done

Still just skimming the list, so ... ;)

I seem to have missed v2 (but I guess the config name changed in v2), but it seems
that there is still no test that confirms '--no-fetch' can countermand the config
variable (or, indeed, an earlier command-line '--fetch' etc,.).

[I would have no use for this facility (I have _never_ used git-pull for similar
reasons) since I would find it odd to 'mash' git-fetch into git-checkout/switch! :) ]

ATB,
Ramsay Jones



^ permalink raw reply

* Re: [PATCH] remote: add --set-head option to 'git remote add'
From: Harald Nordgren @ 2026-04-26 15:15 UTC (permalink / raw)
  To: kristofferhaugsbakk; +Cc: git, gitgitgadget, haraldnordgren
In-Reply-To: <c61933c9-e489-4f4f-b4f4-74267d5eb76f@app.fastmail.com>

> I don’t understand why you change the email subjects so often. Right now
> I had three “Wrong subject line” in my inbox with lost threading
> (webmail client) with the only way to distinguish them being that I was
> the the CC on this one.
> 
> Most of the time whole 100-email threads like patch series never change
> the subject. And to me it is easier to keep track of those “RE: [PATCH
> v5] florb: drop glorb” than if someone changes the subject to e.g.
> “Regarding memory leaks” because someone found a memory leak in a
> review. Because that was a reply to an email from two days ago, but I’ve
> been a away for a week so I think it’s a new thread about
> something else.

I got some feedback before, not from you, that subject lines should be
more varied, probably there is a golden middle that I need to find.

And I fundamentally agree with you.


Harald

^ permalink raw reply

* [PATCH v2 8/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

When an attacker can convince a user to clone a crafted repository
that contains an embedded bare repository with malicious hooks, any Git
command the user runs after entering that subdirectory will discover
the bare repository and execute the hooks. The user does not even need
to run a Git command explicitly: many shell prompts run `git status`
in the background to display branch and dirty state information, and
`git status` in turn may invoke the fsmonitor hook if so configured,
making the user vulnerable the moment they `cd` into the directory. The
`safe.bareRepository` configuration variable (introduced in 8959555cee7e
(setup_git_directory(): add an owner check for the top-level directory,
2022-03-02)) already provides protection against this attack vector by
allowing users to set it to "explicit", but the default remained "all"
for backwards compatibility.

Since Git 3.0 is the natural point to change defaults to safer
values, flip the default from "all" to "explicit" when built with
`WITH_BREAKING_CHANGES`. This means Git will refuse to work with bare
repositories that are discovered implicitly by walking up the directory
tree. Bare repositories specified via `--git-dir` or `GIT_DIR` continue
to work, and directories that look like `.git`, worktrees, or submodule
directories are unaffected (the existing `is_implicit_bare_repo()`
whitelist handles those cases).

Users who rely on implicit bare repository discovery can restore the
previous behavior by setting `safe.bareRepository=all` in their global
or system configuration.

The test for the "safe.bareRepository in the repository" scenario
needed a more involved fix: it writes a `safe.bareRepository=all`
entry into the bare repository's own config to verify that repo-local
config does not override the protected (global) setting. Previously,
`test_config -C` was used to write that entry, but its cleanup runs `git
-C <bare-repo> config --unset`, which itself fails when the default is
"explicit" and the global config has already been cleaned up. Switching
to direct git config --file access avoids going through repository
discovery entirely.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 Documentation/BreakingChanges.adoc | 24 ++++++++++++++++++++++++
 Documentation/config/safe.adoc     | 10 ++++++++--
 setup.c                            |  4 ++++
 t/t0035-safe-bare-repository.sh    | 10 ++++++++--
 4 files changed, 44 insertions(+), 4 deletions(-)

diff --git a/Documentation/BreakingChanges.adoc b/Documentation/BreakingChanges.adoc
index af59c43f42..73bb939359 100644
--- a/Documentation/BreakingChanges.adoc
+++ b/Documentation/BreakingChanges.adoc
@@ -216,6 +216,30 @@ would be significant, we may decide to defer this change to a subsequent minor
 release. This evaluation will also take into account our own experience with
 how painful it is to keep Rust an optional component.
 
+* The default value of `safe.bareRepository` will change from `all` to
+  `explicit`. It is all too easy for an attacker to trick a user into cloning a
+  repository that contains an embedded bare repository with malicious hooks
+  configured. If the user enters that subdirectory and runs any Git command, Git
+  discovers the bare repository and the hooks fire. The user does not even need
+  to run a Git command explicitly: many shell prompts run `git status` in the
+  background to display branch and dirty state information, and `git status` in
+  turn may invoke the fsmonitor hook if so configured, making the user
+  vulnerable the moment they `cd` into the directory. The `safe.bareRepository`
+  configuration variable was introduced in 8959555cee (setup_git_directory():
+  add an owner check for the top-level directory, 2022-03-02) with a default of
+  `all` to preserve backwards compatibility.
++
+Changing the default to `explicit` means that Git will refuse to work with bare
+repositories that are discovered implicitly by walking up the directory tree.
+Bare repositories specified explicitly via the `--git-dir` command-line option
+or the `GIT_DIR` environment variable continue to work regardless of this
+setting. Repositories that look like a `.git` directory, a worktree, or a
+submodule directory are also unaffected.
++
+Users who rely on implicit discovery of bare repositories can restore the
+previous behavior by setting `safe.bareRepository=all` in their global or
+system configuration.
+
 === Removals
 
 * Support for grafting commits has long been superseded by git-replace(1).
diff --git a/Documentation/config/safe.adoc b/Documentation/config/safe.adoc
index 2d45c98b12..5b1690aebe 100644
--- a/Documentation/config/safe.adoc
+++ b/Documentation/config/safe.adoc
@@ -2,10 +2,12 @@ safe.bareRepository::
 	Specifies which bare repositories Git will work with. The currently
 	supported values are:
 +
-* `all`: Git works with all bare repositories. This is the default.
+* `all`: Git works with all bare repositories. This is the default in
+  Git 2.x.
 * `explicit`: Git only works with bare repositories specified via
   the top-level `--git-dir` command-line option, or the `GIT_DIR`
-  environment variable (see linkgit:git[1]).
+  environment variable (see linkgit:git[1]). This will be the default
+  in Git 3.0.
 +
 If you do not use bare repositories in your workflow, then it may be
 beneficial to set `safe.bareRepository` to `explicit` in your global
@@ -13,6 +15,10 @@ config. This will protect you from attacks that involve cloning a
 repository that contains a bare repository and running a Git command
 within that directory.
 +
+If you use bare repositories regularly and want to preserve the current
+behavior after upgrading to Git 3.0, set `safe.bareRepository` to `all`
+in your global or system config.
++
 This config setting is only respected in protected configuration (see
 <<SCOPES>>). This prevents untrusted repositories from tampering with
 this value.
diff --git a/setup.c b/setup.c
index 7ec4427368..17c0662076 100644
--- a/setup.c
+++ b/setup.c
@@ -1485,7 +1485,11 @@ static int allowed_bare_repo_cb(const char *key, const char *value,
 
 static enum allowed_bare_repo get_allowed_bare_repo(void)
 {
+#ifdef WITH_BREAKING_CHANGES
+	enum allowed_bare_repo result = ALLOWED_BARE_REPO_EXPLICIT;
+#else
 	enum allowed_bare_repo result = ALLOWED_BARE_REPO_ALL;
+#endif
 	git_protected_config(allowed_bare_repo_cb, &result);
 	return result;
 }
diff --git a/t/t0035-safe-bare-repository.sh b/t/t0035-safe-bare-repository.sh
index ae7ef092ab..1d3d19f5b4 100755
--- a/t/t0035-safe-bare-repository.sh
+++ b/t/t0035-safe-bare-repository.sh
@@ -44,11 +44,16 @@ test_expect_success 'setup an embedded bare repo, secondary worktree and submodu
 	test_path_is_dir outer-repo/.git/modules/subn
 '
 
-test_expect_success 'safe.bareRepository unset' '
+test_expect_success !WITH_BREAKING_CHANGES 'safe.bareRepository unset' '
 	test_unconfig --global safe.bareRepository &&
 	expect_accepted_implicit -C outer-repo/bare-repo
 '
 
+test_expect_success WITH_BREAKING_CHANGES 'safe.bareRepository unset (defaults to explicit)' '
+	test_unconfig --global safe.bareRepository &&
+	expect_rejected -C outer-repo/bare-repo
+'
+
 test_expect_success 'safe.bareRepository=all' '
 	test_config_global safe.bareRepository all &&
 	expect_accepted_implicit -C outer-repo/bare-repo
@@ -63,7 +68,8 @@ test_expect_success 'safe.bareRepository in the repository' '
 	# safe.bareRepository must not be "explicit", otherwise
 	# git config fails with "fatal: not in a git directory" (like
 	# safe.directory)
-	test_config -C outer-repo/bare-repo safe.bareRepository all &&
+	test_when_finished "git config --file outer-repo/bare-repo/config --unset safe.bareRepository" &&
+	git config --file outer-repo/bare-repo/config safe.bareRepository all &&
 	test_config_global safe.bareRepository explicit &&
 	expect_rejected -C outer-repo/bare-repo
 '
-- 
gitgitgadget

^ permalink raw reply related

* [PATCH v2 7/8] status tests: filter `.gitconfig` from status output
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

Since test-lib.sh creates `$HOME/.gitconfig` when
`WITH_BREAKING_CHANGES` is in effect, the file appears in `git
status` output as either untracked (`?? .gitconfig`) or ignored
(`!! .gitconfig` / `! .gitconfig`, depending on porcelain version),
because the `.git/info/exclude` entry causes git to treat it as an
ignored file rather than hiding it entirely.

In t7061 and t7521, which are pervasively affected, introduce a
`filter_gitconfig` helper that strips all status-prefix variants of
`.gitconfig` from the output before comparison. In the remaining
scripts (t7060, t7064, t7508), apply targeted adjustments.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t7060-wtstatus.sh        |  3 +--
 t/t7061-wtstatus-ignore.sh | 27 +++++++++++++++++++++++++++
 t/t7064-wtstatus-pv2.sh    |  1 +
 t/t7508-status.sh          |  4 ++++
 t/t7521-ignored-mode.sh    |  1 +
 5 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
index 0f4344c55e..942ddbbf0e 100755
--- a/t/t7060-wtstatus.sh
+++ b/t/t7060-wtstatus.sh
@@ -9,6 +9,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 test_expect_success setup '
 	git config --global advice.statusuoption false &&
+	echo "/.gitconfig" >>.git/info/exclude &&
 	test_commit A &&
 	test_commit B oneside added &&
 	git checkout A^0 &&
@@ -221,7 +222,6 @@ test_expect_success 'status --branch with detached HEAD' '
 	git status --branch --porcelain >actual &&
 	cat >expected <<-EOF &&
 	## HEAD (no branch)
-	?? .gitconfig
 	?? actual
 	?? expect
 	?? expected
@@ -237,7 +237,6 @@ test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '
 	git status --branch --porcelain=v1 >actual &&
 	cat >expected <<-EOF &&
 	## HEAD (no branch)
-	?? .gitconfig
 	?? actual
 	?? expect
 	?? expected
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
index 2f9bea9793..14ddaba2f3 100755
--- a/t/t7061-wtstatus-ignore.sh
+++ b/t/t7061-wtstatus-ignore.sh
@@ -18,6 +18,7 @@ test_expect_success 'status untracked directory with --ignored' '
 	: >untracked/ignored &&
 	: >untracked/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -27,6 +28,7 @@ test_expect_success 'same with gitignore starting with BOM' '
 	: >untracked/ignored &&
 	: >untracked/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -40,18 +42,22 @@ test_expect_success 'status untracked files --ignored with pathspec (no match)'
 test_expect_success 'status untracked files --ignored with pathspec (literal match)' '
 	git status --porcelain --ignored -- untracked/ignored >actual &&
 	echo "!! untracked/ignored" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual &&
 	git status --porcelain --ignored -- untracked/uncommitted >actual &&
 	echo "?? untracked/uncommitted" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
 test_expect_success 'status untracked files --ignored with pathspec (glob match)' '
 	git status --porcelain --ignored -- untracked/i\* >actual &&
 	echo "!! untracked/ignored" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual &&
 	git status --porcelain --ignored -- untracked/u\* >actual &&
 	echo "?? untracked/uncommitted" >expected &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -65,6 +71,7 @@ EOF
 
 test_expect_success 'status untracked directory with --ignored -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 cat >expected <<\EOF
@@ -76,9 +83,11 @@ test_expect_success 'status of untracked directory with --ignored works with or
 	git status --porcelain --ignored >tmp &&
 	grep untracked/ tmp >actual &&
 	rm tmp &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual &&
 
 	git status --porcelain --ignored untracked/ >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -89,6 +98,7 @@ EOF
 
 test_expect_success 'status prefixed untracked sub-directory with --ignored -u' '
 	git status --porcelain --ignored -u untracked/ >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -104,6 +114,7 @@ test_expect_success 'status ignored directory with --ignore' '
 	mkdir ignored &&
 	: >ignored/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -116,6 +127,7 @@ EOF
 
 test_expect_success 'status ignored directory with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -130,6 +142,7 @@ test_expect_success 'status empty untracked directory with --ignore' '
 	mkdir untracked-ignored &&
 	mkdir untracked-ignored/test &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -141,6 +154,7 @@ EOF
 
 test_expect_success 'status empty untracked directory with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -155,6 +169,7 @@ test_expect_success 'status untracked directory with ignored files with --ignore
 	: >untracked-ignored/ignored &&
 	: >untracked-ignored/test/ignored &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -168,6 +183,7 @@ EOF
 
 test_expect_success 'status untracked directory with ignored files with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -185,6 +201,7 @@ test_expect_success 'status ignored tracked directory with --ignore' '
 	git commit -m. &&
 	echo "tracked" >.gitignore &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -196,6 +213,7 @@ EOF
 
 test_expect_success 'status ignored tracked directory with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -208,6 +226,7 @@ EOF
 test_expect_success 'status ignored tracked directory and ignored file with --ignore' '
 	echo "committed" >>.gitignore &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -219,6 +238,7 @@ EOF
 
 test_expect_success 'status ignored tracked directory and ignored file with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -233,6 +253,7 @@ test_expect_success 'status ignored tracked directory and uncommitted file with
 	echo "tracked" >.gitignore &&
 	: >tracked/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -245,6 +266,7 @@ EOF
 
 test_expect_success 'status ignored tracked directory and uncommitted file with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -260,6 +282,7 @@ test_expect_success 'status ignored tracked directory with uncommitted file in u
 	mkdir tracked/ignored &&
 	: >tracked/ignored/uncommitted &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -272,6 +295,7 @@ EOF
 
 test_expect_success 'status ignored tracked directory with uncommitted file in untracked subdir with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -287,6 +311,7 @@ test_expect_success 'status ignored tracked directory with uncommitted file in t
 	git add -f tracked/ignored/committed &&
 	git commit -m. &&
 	git status --porcelain --ignored >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -299,6 +324,7 @@ EOF
 
 test_expect_success 'status ignored tracked directory with uncommitted file in tracked subdir with --ignore -u' '
 	git status --porcelain --ignored -u >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
@@ -310,6 +336,7 @@ test_expect_success 'status ignores submodule in excluded directory' '
 	git init tracked/submodule &&
 	test_commit -C tracked/submodule initial &&
 	git status --porcelain --ignored -u tracked/submodule >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expected actual
 '
 
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 8bbc5ce6d9..be6c931a96 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -231,6 +231,7 @@ test_expect_success 'ignored files are printed with --ignored' '
 	EOF
 
 	git status --porcelain=v2 --ignored --untracked-files=all >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index a5e21bf8bf..5f76ec62d8 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -263,6 +263,7 @@ test_expect_success 'status with gitignore' '
 	!! untracked
 	EOF
 	git status -s --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output &&
 
 	cat >expect <<\EOF &&
@@ -296,6 +297,7 @@ Ignored files:
 
 EOF
 	git status --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
@@ -328,6 +330,7 @@ test_expect_success 'status with gitignore (nothing untracked)' '
 	!! untracked
 	EOF
 	git status -s --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output &&
 
 	cat >expect <<\EOF &&
@@ -358,6 +361,7 @@ Ignored files:
 
 EOF
 	git status --ignored >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
diff --git a/t/t7521-ignored-mode.sh b/t/t7521-ignored-mode.sh
index a88b02b06e..7ea0b0d2f2 100755
--- a/t/t7521-ignored-mode.sh
+++ b/t/t7521-ignored-mode.sh
@@ -30,6 +30,7 @@ test_expect_success 'Verify behavior of status on directories with ignored files
 		dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
 	git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 6/8] ls-files tests: filter `.gitconfig` from `--others` output
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

The global `safe.bareRepository=all` setting in test-lib.sh is
written to `$HOME/.gitconfig`, which unfortunately lives inside the
test repository's working tree. The `.git/info/exclude` entry added
alongside it handles most commands, but `git ls-files --others`
without `--exclude-standard` does not consult `info/exclude` at
all, so the file appears in the output.

Ideally, each test that accesses a bare repository would simply
specify `--git-dir` or `GIT_DIR` explicitly, which would require no
global config and produce no side effects in the working tree. As
that approach was not taken, filter `.gitconfig` from the output
before comparing against expected results. In t7104, the test
already uses `--exclude-standard`, so it suffices to switch from
the bare `git ls-files -o` to `git ls-files -o --exclude-standard`
which respects the `info/exclude` entry; the other tests
deliberately omit `--exclude-standard` because their purpose is to
verify unfiltered `--others` output.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t3000-ls-files-others.sh                         | 4 ++++
 t/t3001-ls-files-others-exclude.sh                 | 3 +++
 t/t3002-ls-files-dashpath.sh                       | 2 ++
 t/t3009-ls-files-others-nonsubmodule.sh            | 1 +
 t/t3011-common-prefixes-and-directory-traversal.sh | 3 ++-
 t/t7104-reset-hard.sh                              | 2 +-
 t/test-lib-functions.sh                            | 8 ++++++++
 7 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/t/t3000-ls-files-others.sh b/t/t3000-ls-files-others.sh
index b41e7f0daa..b4f0fbfc55 100755
--- a/t/t3000-ls-files-others.sh
+++ b/t/t3000-ls-files-others.sh
@@ -53,16 +53,19 @@ test_expect_success 'setup: expected output' '
 
 test_expect_success 'ls-files --others' '
 	git ls-files --others >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected1 output
 '
 
 test_expect_success 'ls-files --others --directory' '
 	git ls-files --others --directory >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected2 output
 '
 
 test_expect_success '--no-empty-directory hides empty directory' '
 	git ls-files --others --directory --no-empty-directory >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected3 output
 '
 
@@ -70,6 +73,7 @@ test_expect_success 'ls-files --others handles non-submodule .git' '
 	mkdir not-a-submodule &&
 	echo foo >not-a-submodule/.git &&
 	git ls-files -o >output &&
+	test_filter_gitconfig output &&
 	test_cmp expected1 output
 '
 
diff --git a/t/t3001-ls-files-others-exclude.sh b/t/t3001-ls-files-others-exclude.sh
index 4b67646285..202fb8d9ea 100755
--- a/t/t3001-ls-files-others-exclude.sh
+++ b/t/t3001-ls-files-others-exclude.sh
@@ -72,6 +72,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
        --exclude-per-directory=.gitignore \
        --exclude-from=.git/ignore \
 	>output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
@@ -84,6 +85,7 @@ test_expect_success 'git ls-files --others with \r\n line endings.' '
        --exclude-per-directory=.gitignore \
        --exclude-from=.git/ignore \
 	>output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
@@ -99,6 +101,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
        --exclude-per-directory=.gitignore \
        --exclude-from=.git/ignore \
 	>output &&
+	test_filter_gitconfig output &&
 	test_cmp expect output
 '
 
diff --git a/t/t3002-ls-files-dashpath.sh b/t/t3002-ls-files-dashpath.sh
index 31462cb441..6acaadbd67 100755
--- a/t/t3002-ls-files-dashpath.sh
+++ b/t/t3002-ls-files-dashpath.sh
@@ -24,6 +24,7 @@ test_expect_success 'setup' '
 test_expect_success 'git ls-files without path restriction.' '
 	test_when_finished "rm -f expect" &&
 	git ls-files --others >output &&
+	test_filter_gitconfig output &&
 	cat >expect <<-\EOF &&
 	--
 	-foo
@@ -63,6 +64,7 @@ test_expect_success 'git ls-files with path restriction with -- --.' '
 test_expect_success 'git ls-files with no path restriction.' '
 	test_when_finished "rm -f expect" &&
 	git ls-files --others -- >output &&
+	test_filter_gitconfig output &&
 	cat >expect <<-\EOF &&
 	--
 	-foo
diff --git a/t/t3009-ls-files-others-nonsubmodule.sh b/t/t3009-ls-files-others-nonsubmodule.sh
index 963f3462b7..dc990c277b 100755
--- a/t/t3009-ls-files-others-nonsubmodule.sh
+++ b/t/t3009-ls-files-others-nonsubmodule.sh
@@ -36,6 +36,7 @@ test_expect_success 'setup: directories' '
 
 test_expect_success 'ls-files --others handles untracked git repositories' '
 	git ls-files -o >output &&
+	test_filter_gitconfig output &&
 	cat >expect <<-EOF &&
 	nonrepo-untracked-file/untracked
 	output
diff --git a/t/t3011-common-prefixes-and-directory-traversal.sh b/t/t3011-common-prefixes-and-directory-traversal.sh
index 3da5b2b6e7..455e97954d 100755
--- a/t/t3011-common-prefixes-and-directory-traversal.sh
+++ b/t/t3011-common-prefixes-and-directory-traversal.sh
@@ -26,7 +26,7 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'git ls-files -o shows the right entries' '
-	cat <<-EOF >expect &&
+	cat >expect <<-EOF &&
 	.gitignore
 	actual
 	an_ignored_dir/ignored
@@ -39,6 +39,7 @@ test_expect_success 'git ls-files -o shows the right entries' '
 	untracked_repo/
 	EOF
 	git ls-files -o >actual &&
+	test_filter_gitconfig actual &&
 	test_cmp expect actual
 '
 
diff --git a/t/t7104-reset-hard.sh b/t/t7104-reset-hard.sh
index 7948ec392b..c23d6e3f52 100755
--- a/t/t7104-reset-hard.sh
+++ b/t/t7104-reset-hard.sh
@@ -21,7 +21,7 @@ test_expect_success setup '
 	rm -f hello &&
 	mkdir -p hello &&
 	>hello/world &&
-	test "$(git ls-files -o)" = hello/world
+	test "$(git ls-files -o --exclude-standard)" = hello/world
 
 '
 
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index f3af10fb7e..0505da78e8 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -2069,3 +2069,11 @@ test_trailing_hash () {
 test_redact_non_printables () {
     tr -d "\n\r" | tr "[\001-\040][\177-\377]" "."
 }
+
+# Remove .gitconfig entries from a file in place.  test-lib.sh may
+# create $HOME/.gitconfig (e.g. to set safe.bareRepository) which
+# can appear in ls-files or status output.
+test_filter_gitconfig () {
+	sed "/\\.gitconfig/d" "$1" >"$1.filtered" &&
+	mv "$1.filtered" "$1"
+}
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 5/8] t5601: restore `.gitconfig` after includeIf test
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

One test in t5601 overwrites `$HOME/.gitconfig` with an `includeIf`
configuration snippet and removes the file in its cleanup. This
destroys the `safe.bareRepository=all` entry that test-lib.sh
writes when `WITH_BREAKING_CHANGES` is in effect, causing later
tests that use `git -C <bare-repo> config` to fail with "not in a
git directory".

Back up `.gitconfig` before overwriting and restore it in the
cleanup, so the global config survives into subsequent tests.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t5601-clone.sh | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index d743d986c4..3dd229c186 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -813,7 +813,9 @@ test_expect_success 'clone with includeIf' '
 	test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
 	git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 
-	test_when_finished "rm \"$HOME\"/.gitconfig" &&
+	test_when_finished "cp \"$HOME\"/.gitconfig.bak \
+		\"$HOME\"/.gitconfig 2>/dev/null || rm -f \"$HOME\"/.gitconfig" &&
+	cp "$HOME"/.gitconfig "$HOME"/.gitconfig.bak 2>/dev/null &&
 	cat >"$HOME"/.gitconfig <<-EOF &&
 	[includeIf "onbranch:something"]
 		path = /does/not/exist.inc
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 4/8] t1305: use `--git-dir=.` for bare repo in include cycle test
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

Earlier tests in t1305 overwrite `$HOME/.gitconfig` with their own
content as part of testing config includes. This clobbers the
`safe.bareRepository=all` entry that test-lib.sh writes when
`WITH_BREAKING_CHANGES` is in effect, causing `git -C cycle config`
to fail with "not in a git directory" when it tries to access the
bare repository created by `git init --bare cycle`.

Use `--git-dir=.` to access the bare repo explicitly, avoiding the
dependency on global config for repository discovery.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t1305-config-include.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 6e51f892f3..f3892578e4 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -350,9 +350,9 @@ test_expect_success 'conditional include, onbranch, implicit /** for /' '
 
 test_expect_success 'include cycles are detected' '
 	git init --bare cycle &&
-	git -C cycle config include.path cycle &&
+	git -C cycle --git-dir=. config include.path cycle &&
 	git config -f cycle/cycle include.path config &&
-	test_must_fail git -C cycle config --get-all test.value 2>stderr &&
+	test_must_fail git -C cycle --git-dir=. config --get-all test.value 2>stderr &&
 	grep "exceeded maximum include depth" stderr
 '
 
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 3/8] t1300: remove global config settings injected by test-lib.sh
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

Since test-lib.sh now writes `safe.bareRepository=all` to the global
config when `WITH_BREAKING_CHANGES` is in effect, that entry shows
up in `git config --list` output. Tests in t1300 that expect exact
config contents then fail because of this unexpected extra line.

Unlike the working-tree contamination fixed in the preceding
commits, this is not about the file's existence but about its
content leaking into test expectations. Since t1300 does not use
bare repositories, simply remove the injected setting in a
preparatory step.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Assisted-by: Claude Opus 4.6
---
 t/t1300-config.sh | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 128971ee12..11fc976f3a 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -11,6 +11,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
+# test-lib.sh may have added global config (e.g. safe.bareRepository)
+# that would appear in "git config --list" output and break tests
+# that expect exact config contents.
+test_expect_success 'remove global config from test-lib.sh' '
+	test_might_fail git config --global --unset-all safe.bareRepository
+'
+
 for mode in legacy subcommands
 do
 
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 2/8] t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

The XDG config tests for `git maintenance register/unregister`
create a fresh `$XDG_CONFIG_HOME/git/config` and expect git to use
that location. However, if `$HOME/.gitconfig` exists (which may
happen when test-lib.sh writes global config, e.g. to set
`safe.bareRepository`), git prefers `$HOME/.gitconfig` over the XDG
location, and the `maintenance.repo` entry ends up in the wrong
file.

This is an inherent consequence of setting global config in
test-lib.sh rather than adjusting individual tests: writing any
entry to `$HOME/.gitconfig` has side effects beyond the intended
setting, because the mere existence of that file changes which
global config location git prefers for all subsequent writes.
Individual per-test adjustments would not have this interaction.

Fix this by overriding `HOME` to a non-existent directory inside the
subshells that test XDG behavior. Since these subshells already
override `XDG_CONFIG_HOME`, they do not need `$HOME/.gitconfig` at
all, and the subshell scoping ensures the original `HOME` is
restored automatically.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/t7900-maintenance.sh | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 4700beacc1..4358df0424 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -101,8 +101,12 @@ test_expect_success "maintenance.autoDetach overrides gc.autoDetach" '
 test_expect_success 'register uses XDG_CONFIG_HOME config if it exists' '
 	test_when_finished rm -r .config/git/config &&
 	(
+		# Override HOME so that .gitconfig (which test-lib.sh may
+		# have created, e.g. to set safe.bareRepository) does not
+		# take precedence over the XDG location.
+		HOME=$PWD/must-not-exist &&
 		XDG_CONFIG_HOME=.config &&
-		export XDG_CONFIG_HOME &&
+		export HOME XDG_CONFIG_HOME &&
 		mkdir -p $XDG_CONFIG_HOME/git &&
 		>$XDG_CONFIG_HOME/git/config &&
 		git maintenance register &&
@@ -124,8 +128,12 @@ test_expect_success 'register does not need XDG_CONFIG_HOME config to exist' '
 test_expect_success 'unregister uses XDG_CONFIG_HOME config if it exists' '
 	test_when_finished rm -r .config/git/config &&
 	(
+		# Override HOME so that .gitconfig (which test-lib.sh may
+		# have created, e.g. to set safe.bareRepository) does not
+		# take precedence over the XDG location.
+		HOME=$PWD/must-not-exist &&
 		XDG_CONFIG_HOME=.config &&
-		export XDG_CONFIG_HOME &&
+		export HOME XDG_CONFIG_HOME &&
 		mkdir -p $XDG_CONFIG_HOME/git &&
 		>$XDG_CONFIG_HOME/git/config &&
 		git maintenance register &&
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 1/8] test-lib: allow bare repository access when breaking changes are enabled
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin, Johannes Schindelin
In-Reply-To: <pull.2098.v2.git.1777214316.gitgitgadget@gmail.com>

From: Johannes Schindelin <johannes.schindelin@gmx.de>

A future patch will change the `safe.bareRepository` default from
`all` to `explicit` under `WITH_BREAKING_CHANGES`. At that point,
every test that operates on a bare repository through implicit
discovery would fail, regardless of whether the test is actually
about discovery or about how a specific command behaves once inside
a bare repository.

The maintainer suggested [1] setting `safe.bareRepository=all` in
the test environment's global config whenever `WITH_BREAKING_CHANGES`
is in effect, rather than adjusting each affected test to access
bare repositories explicitly (via `--git-dir`, `GIT_DIR`, or
similar). This means the test suite continues to exercise only the
historical default behavior even after the user-facing default
changes, relying on a small number of dedicated tests in t0035 to
validate the new, stricter default.

Since `$HOME` points at the trash directory (which doubles as the
test repository's working tree), writing to `$HOME/.gitconfig` also
creates a file inside the working tree. Exclude it via
`.git/info/exclude` to limit the fallout, though this does not
help tests that use `git ls-files --others` without
`--exclude-standard` or `git status --ignored`; those are addressed
by subsequent commits.

The `.git/info/exclude` write is guarded by `test -d .git/info`
rather than using `mkdir -p`, because some tests (e.g. t0008)
expect to create `.git/info/` themselves and would fail with
Patrick Steinhardt's `set -e` preparation (ps/test-set-e-clean) if
the directory already existed. For tests using `TEST_NO_CREATE_REPO`
(where no `.git/` exists at all), the guard also handles that case.

[1] https://lore.kernel.org/git/xmqqse98cc51.fsf@gitster.g/

Original-patch-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 t/test-lib.sh | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/t/test-lib.sh b/t/test-lib.sh
index 70fd3e9baf..72ed87b781 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -1597,6 +1597,19 @@ cd -P "$TRASH_DIRECTORY" || BAIL_OUT "cannot cd -P to \"$TRASH_DIRECTORY\""
 TRASH_DIRECTORY=$(pwd)
 HOME="$TRASH_DIRECTORY"
 
+if test -n "$WITH_BREAKING_CHANGES"
+then
+	git config --global safe.bareRepository all &&
+	# Only write to .git/info/exclude when the directory exists
+	# (i.e. when git init created the repo). If we mkdir -p it
+	# ourselves, tests that expect to create .git/info/ themselves
+	# (e.g. t0008) would fail.
+	if test -d .git/info
+	then
+		echo "/.gitconfig" >>.git/info/exclude
+	fi
+fi
+
 start_test_output "$0"
 
 # Convenience
-- 
gitgitgadget


^ permalink raw reply related

* [PATCH v2 0/8] safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
From: Johannes Schindelin via GitGitGadget @ 2026-04-26 14:38 UTC (permalink / raw)
  To: git; +Cc: Johannes Schindelin
In-Reply-To: <pull.2098.git.1777042877.gitgitgadget@gmail.com>

This supersedes my earlier series [*1*] which took the approach of adjusting
individual tests to access bare repositories explicitly.

As Junio suggested [*2*], this series instead takes the approach of setting
safe.bareRepository=all in the test environment's global config whenever
WITH_BREAKING_CHANGES is in effect, so that existing tests continue to work
without individual modifications.

Implementing this turned out to require a number of follow-up adjustments,
because writing to $HOME/.gitconfig has side effects beyond the intended
setting: $HOME is the trash directory, which doubles as the test
repository's working tree, so the file shows up in ls-files and status
output, and tests that manipulate $HOME/.gitconfig for their own purposes
can clobber or remove the setting. Patches 2 through 7 address these
interactions in the affected test scripts.

The final patch flips the safe.bareRepository default to "explicit" under
WITH_BREAKING_CHANGES.

Footnote [*1*]:
https://lore.kernel.org/git/pull.2076.git.1775140403.gitgitgadget@gmail.com/

Footnote [*2*]: https://lore.kernel.org/git/xmqqse98cc51.fsf@gitster.g/

Changes since v1:

 * Made it compatible with Patrick Steinhardt's set -e work.

Johannes Schindelin (8):
  test-lib: allow bare repository access when breaking changes are
    enabled
  t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
  t1300: remove global config settings injected by test-lib.sh
  t1305: use `--git-dir=.` for bare repo in include cycle test
  t5601: restore `.gitconfig` after includeIf test
  ls-files tests: filter `.gitconfig` from `--others` output
  status tests: filter `.gitconfig` from status output
  safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES

 Documentation/BreakingChanges.adoc            | 24 +++++++++++++++++
 Documentation/config/safe.adoc                | 10 +++++--
 setup.c                                       |  4 +++
 t/t0035-safe-bare-repository.sh               | 10 +++++--
 t/t1300-config.sh                             |  7 +++++
 t/t1305-config-include.sh                     |  4 +--
 t/t3000-ls-files-others.sh                    |  4 +++
 t/t3001-ls-files-others-exclude.sh            |  3 +++
 t/t3002-ls-files-dashpath.sh                  |  2 ++
 t/t3009-ls-files-others-nonsubmodule.sh       |  1 +
 ...common-prefixes-and-directory-traversal.sh |  3 ++-
 t/t5601-clone.sh                              |  4 ++-
 t/t7060-wtstatus.sh                           |  3 +--
 t/t7061-wtstatus-ignore.sh                    | 27 +++++++++++++++++++
 t/t7064-wtstatus-pv2.sh                       |  1 +
 t/t7104-reset-hard.sh                         |  2 +-
 t/t7508-status.sh                             |  4 +++
 t/t7521-ignored-mode.sh                       |  1 +
 t/t7900-maintenance.sh                        | 12 +++++++--
 t/test-lib-functions.sh                       |  8 ++++++
 t/test-lib.sh                                 | 13 +++++++++
 21 files changed, 134 insertions(+), 13 deletions(-)


base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2098%2Fdscho%2Fsafe-bare-repo-default-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2098/dscho/safe-bare-repo-default-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/2098

Range-diff vs v1:

 1:  62707b4109 ! 1:  179fcf5369 test-lib: allow bare repository access when breaking changes are enabled
     @@ Commit message
          `--exclude-standard` or `git status --ignored`; those are addressed
          by subsequent commits.
      
     +    The `.git/info/exclude` write is guarded by `test -d .git/info`
     +    rather than using `mkdir -p`, because some tests (e.g. t0008)
     +    expect to create `.git/info/` themselves and would fail with
     +    Patrick Steinhardt's `set -e` preparation (ps/test-set-e-clean) if
     +    the directory already existed. For tests using `TEST_NO_CREATE_REPO`
     +    (where no `.git/` exists at all), the guard also handles that case.
     +
          [1] https://lore.kernel.org/git/xmqqse98cc51.fsf@gitster.g/
      
          Original-patch-by: Junio C Hamano <gitster@pobox.com>
     @@ t/test-lib.sh: cd -P "$TRASH_DIRECTORY" || BAIL_OUT "cannot cd -P to \"$TRASH_DI
      +if test -n "$WITH_BREAKING_CHANGES"
      +then
      +	git config --global safe.bareRepository all &&
     -+	echo "/.gitconfig" >>.git/info/exclude
     ++	# Only write to .git/info/exclude when the directory exists
     ++	# (i.e. when git init created the repo). If we mkdir -p it
     ++	# ourselves, tests that expect to create .git/info/ themselves
     ++	# (e.g. t0008) would fail.
     ++	if test -d .git/info
     ++	then
     ++		echo "/.gitconfig" >>.git/info/exclude
     ++	fi
      +fi
      +
       start_test_output "$0"
 2:  d9a2e76f3c = 2:  4dc5151e59 t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
 3:  9c10e72eed = 3:  7d68155805 t1300: remove global config settings injected by test-lib.sh
 4:  092ec11621 = 4:  5ff48e0892 t1305: use `--git-dir=.` for bare repo in include cycle test
 5:  3aca302275 = 5:  ed7294ace3 t5601: restore `.gitconfig` after includeIf test
 6:  ef57244778 = 6:  556db0eabe ls-files tests: filter `.gitconfig` from `--others` output
 7:  56fe902644 = 7:  ac4da79eac status tests: filter `.gitconfig` from status output
 8:  64db45e385 = 8:  73bb1aa171 safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES

-- 
gitgitgadget

^ permalink raw reply

* Re: [PATCH] parse-options: fix sparse 'plain integer as NULL pointer'
From: Johannes Schindelin @ 2026-04-26 12:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Ramsay Jones, Jiamu Sun, GIT Mailing-list
In-Reply-To: <CAPc5daWZUJA0sEqS4Qk2vn5x+LNRgphxjzjdbcLFpEgS+69kUg@mail.gmail.com>

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

Hi Junio,

On Sun, 26 Apr 2026, Junio C Hamano wrote:

> Another thing from GitHub CI
> 
> https://github.com/git/git/actions/runs/24825391649/job/72659919418#step:9:144
> 
>   Error: parse-options.c:680:30: comparison of integer expressions of
> different signedness: 'unsigned int' and 'int' [-Werror=sign-compare]
>     680 |        (n < cmds->nr && best == (intptr_t)cmds->items[n].util);
>         |                              ^~

Indeed. With this patch on top of Ramsay's fixup, it passes the build:
https://github.com/dscho/git/actions/runs/24955417618

-- snip --
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Sun, 26 Apr 2026 11:11:35 +0000
Subject: [PATCH] fixup! parseopt: autocorrect mistyped subcommands

Fix a build warning in `linux32`: Change `best` from `unsigned int` to
`intptr_t` to match the `(intptr_t)` casts it is compared against, fixing
a sign-compare warning on 32-bit platforms where `intptr_t` is `int`.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 parse-options.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/parse-options.c b/parse-options.c
index 14f3f385eb4..0ba6905fed5 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -648,7 +648,8 @@ static const char *autocorrect_subcommand(const char *cmd,
 					  struct string_list *cmds)
 {
 	struct autocorrect autocorrect = { 0 };
-	unsigned int n = 0, best = 0;
+	unsigned int n = 0;
+	intptr_t best = 0;
 	struct string_list_item *cand;
 
 	autocorrect_resolve(&autocorrect);
-- snap --

Ciao,
Johannes

> 
> 2026年4月24日(金) 1:05 Ramsay Jones <ramsay@ramsayjones.plus.com>:
> >
> >
> > Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>
> > ---
> >
> > Hi Jiamu Sun,
> >
> > If you need to re-roll your 'js/parseopt-subcommand-autocorrection'
> > branch, could you please squash this into the patch corresponding
> > to commit b9e6a2d30a ("parseopt: autocorrect mistyped subcommands",
> > 2026-04-23).
> >
> > Thanks.
> >
> > ATB,
> > Ramsay Jones
> >
> >  parse-options.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/parse-options.c b/parse-options.c
> > index d60e7bd3c9..14f3f385eb 100644
> > --- a/parse-options.c
> > +++ b/parse-options.c
> > @@ -658,7 +658,7 @@ static const char *autocorrect_subcommand(const char *cmd,
> >
> >         for_each_string_list_item(cand, cmds) {
> >                 if (starts_with(cand->string, cmd)) {
> > -                       cand->util = 0;
> > +                       cand->util = NULL;
> >                 } else {
> >                         int edit = levenshtein(cmd, cand->string,
> >                                                0, 2, 1, 3) + 1;
> > --
> > 2.54.0
> 
> 

^ permalink raw reply related

* [BUG] git check-ignore --help doesn't cover -z/--stdin conflict
From: David Le @ 2026-04-26 11:28 UTC (permalink / raw)
  To: git

Hi all,

Just reporting a minor issue between check-ignore and its docs.

What did you do before the bug happened? (Steps to reproduce your issue)

- Be in a git repo so check-ignore will succeed
- Run git check-ignore -z <pathname>...

What did you expect to happen? (Expected behavior)

The command should work, printing the ignored paths out of those given,
delimited by NUL characters.

What happened instead? (Actual behavior)

git check-ignore -z <pathname>... fails with:
    fatal: -z only makes sense with --stdin

Anything else you want to add:

Currently git check-ignore --help implies that -z works independently of
--stdin (specifically, "If --stdin is also given"):

   -z
       The output format is modified to be machine-parsable (see below).
       If --stdin is also given, input paths are separated with a NUL
       character instead of a linefeed character.

Either patching the quoted documentation to match check-ignore, or vice
versa, makes sense to me.

[System Info]
git version:
git version 2.54.0
cpu: x86_64
built from commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
rust: enabled
gettext: enabled
libcurl: 8.19.0
OpenSSL: OpenSSL 3.6.2 7 Apr 2026
zlib-ng: 2.3.3
SHA-1: SHA1_DC
SHA-256: SHA256_BLK
default-ref-format: files
default-hash: sha1
uname: Linux 6.19.12-arch1-1 #1 SMP PREEMPT_DYNAMIC Sat, 11 Apr 2026
22:36:29 +0000 x86_64
compiler info: gnuc: 15.2
libc info: glibc: 2.43
$SHELL (typically, interactive shell): /usr/bin/bash


[Enabled Hooks]

---
pynappo

^ permalink raw reply

* Re: [PATCH] remote: add --set-head option to 'git remote add'
From: Harald Nordgren @ 2026-04-26  8:21 UTC (permalink / raw)
  To: peff; +Cc: git, gitgitgadget, gitster, haraldnordgren
In-Reply-To: <20260425220629.GA28590@coredump.intra.peff.net>

> It already does, doesn't it? Doing:
> 
>   $ git init
>   $ git remote add --fetch origin /path/to/some/repo
>   $ git for-each-ref
> 
> shows an origin/HEAD link.
> 
> Which I think is not too surprising, as it is just calling "git fetch"
> under the hood.

I think you are right! So maybe all of this new code is not needed at all.


Harald

^ permalink raw reply

* [PATCH v3] checkout: add --fetch to fetch remote before resolving start-point
From: Harald Nordgren via GitGitGadget @ 2026-04-26  7:24 UTC (permalink / raw)
  To: git
  Cc: Ramsay Jones, D. Ben Knoble, Kristoffer Haugsbakk, Marc Branchaud,
	Harald Nordgren, Harald Nordgren
In-Reply-To: <pull.2281.v2.git.git.1777140755373.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

A common workflow is:

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

The first command exists purely so the second sees an up-to-date view
of the remote. If it is forgotten, origin/some-branch points at a stale
commit and the new local branch is created from the wrong start point.

Teach checkout (and switch) a --fetch flag that folds the two steps
into one:

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

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

Also add a checkout.fetch config to enable this by default.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
    checkout: add --fetch to fetch remote before resolving start-point
    
    When <start-point> is in <remote>/<branch> form, only fetch that one
    branch instead of the whole remote, so unrelated remote-tracking
    branches stay stable. The bare-remote form (e.g. "origin") still fetches
    everything.

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

Range-diff vs v2:

 1:  13074c9fea ! 1:  df7b63862c checkout: add --fetch to fetch remote before resolving start-point
     @@ Commit message
      
              git checkout --fetch -b new_branch origin/some-branch
      
     -    When --fetch is given and <start-point> names a configured remote
     -    (either bare, like "origin", or prefixed, like "origin/foo"), fetch
     -    that remote before resolving the ref. Abort the checkout if the fetch
     -    fails.
     +    When --fetch is given and <start-point> is in <remote>/<branch> form,
     +    run "git fetch <remote> <branch>" before resolving the ref. This
     +    narrows the fetch to the requested branch so that other
     +    remote-tracking branches are left untouched -- many tools rely on the
     +    stability of remote-tracking refs between explicit fetches. If
     +    <start-point> is a bare remote name like "origin" (which resolves to
     +    that remote's default branch), "git fetch <remote>" is run instead,
     +    since the target branch is not known up front. Abort the checkout if
     +    the fetch fails.
      
          Also add a checkout.fetch config to enable this by default.
      
     @@ Documentation/git-checkout.adoc: linkgit:git-config[1].
       
      +`--fetch`::
      +`--no-fetch`::
     -+	If _<start-point>_ names a configured remote -- either bare,
     -+	like `origin` (which resolves to the remote's default branch),
     -+	or in _<remote>/<branch>_ form -- run `git fetch` on that
     -+	remote before resolving _<start-point>_. If the fetch fails,
     -+	the checkout is aborted and no local branch is created.
     ++	If _<start-point>_ refers to a remote-tracking branch, fetch
     ++	from that remote before resolving it. When _<start-point>_ is
     ++	in _<remote>/<branch>_ form, only that branch is updated; when
     ++	it is a bare remote name (e.g. `origin`), the whole remote is
     ++	fetched. If the fetch fails, the checkout is aborted.
      ++
      +The default behavior can be set via the `checkout.fetch` configuration
      +variable.
     @@ Documentation/git-switch.adoc: ambiguous but exists on the 'origin' remote. See
       
      +`--fetch`::
      +`--no-fetch`::
     -+	If _<start-point>_ names a configured remote -- either bare,
     -+	like `origin` (which resolves to the remote's default branch),
     -+	or in _<remote>/<branch>_ form -- run `git fetch` on that
     -+	remote before resolving _<start-point>_. If the fetch fails,
     -+	the switch is aborted and no local branch is created.
     ++	If _<start-point>_ refers to a remote-tracking branch, fetch
     ++	from that remote before resolving it. When _<start-point>_ is
     ++	in _<remote>/<branch>_ form, only that branch is updated; when
     ++	it is a bare remote name (e.g. `origin`), the whole remote is
     ++	fetched. If the fetch fails, the switch is aborted.
      ++
      +The default behavior can be set via the `checkout.fetch` configuration
      +variable.
     @@ builtin/checkout.c: struct branch_info {
      +	}
      +
      +	strvec_pushl(&cmd.args, "fetch", remote_name, NULL);
     ++	if (slash && slash[1])
     ++		strvec_push(&cmd.args, slash + 1);
      +	cmd.git_cmd = 1;
      +	free(remote_name);
      +	if (run_command(&cmd))
     @@ t/t7201-co.sh: test_expect_success 'tracking info copied with autoSetupMerge=inh
      +	test_cmp_rev refs/remotes/fetch_upstream/fetch_new HEAD
      +'
      +
     ++test_expect_success 'checkout --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 --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 --fetch with bare remote name fetches the remote' '
      +	git checkout main &&
      +	git -C fetch_upstream checkout -b fetch_new2 &&


 Documentation/config/checkout.adoc |  5 +++
 Documentation/git-checkout.adoc    | 11 +++++
 Documentation/git-switch.adoc      | 11 +++++
 builtin/checkout.c                 | 50 +++++++++++++++++++++-
 t/t7201-co.sh                      | 68 ++++++++++++++++++++++++++++++
 t/t9902-completion.sh              |  1 +
 6 files changed, 144 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/checkout.adoc b/Documentation/config/checkout.adoc
index e35d212969..c95f72b38e 100644
--- a/Documentation/config/checkout.adoc
+++ b/Documentation/config/checkout.adoc
@@ -22,6 +22,11 @@ commands or functionality in the future.
 	option in `git checkout` and `git switch`. See
 	linkgit:git-switch[1] and linkgit:git-checkout[1].
 
+`checkout.fetch`::
+	Provides the default value for the `--fetch` or `--no-fetch`
+	option in `git checkout` and `git switch`. See
+	linkgit:git-switch[1] and linkgit:git-checkout[1].
+
 `checkout.workers`::
 	The number of parallel workers to use when updating the working tree.
 	The default is one, i.e. sequential execution. If set to a value less
diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc
index 43ccf47cf6..f5cc1ced74 100644
--- a/Documentation/git-checkout.adoc
+++ b/Documentation/git-checkout.adoc
@@ -201,6 +201,17 @@ linkgit:git-config[1].
 The default behavior can be set via the `checkout.guess` configuration
 variable.
 
+`--fetch`::
+`--no-fetch`::
+	If _<start-point>_ refers to a remote-tracking branch, fetch
+	from that remote before resolving it. When _<start-point>_ is
+	in _<remote>/<branch>_ form, only that branch is updated; when
+	it is a bare remote name (e.g. `origin`), the whole remote is
+	fetched. If the fetch fails, the checkout is aborted.
++
+The default behavior can be set via the `checkout.fetch` configuration
+variable.
+
 `-l`::
 	Create the new branch's reflog; see linkgit:git-branch[1] for
 	details.
diff --git a/Documentation/git-switch.adoc b/Documentation/git-switch.adoc
index 87707e9265..29743bafea 100644
--- a/Documentation/git-switch.adoc
+++ b/Documentation/git-switch.adoc
@@ -110,6 +110,17 @@ ambiguous but exists on the 'origin' remote. See also
 The default behavior can be set via the `checkout.guess` configuration
 variable.
 
+`--fetch`::
+`--no-fetch`::
+	If _<start-point>_ refers to a remote-tracking branch, fetch
+	from that remote before resolving it. When _<start-point>_ is
+	in _<remote>/<branch>_ form, only that branch is updated; when
+	it is a bare remote name (e.g. `origin`), the whole remote is
+	fetched. If the fetch fails, the switch is aborted.
++
+The default behavior can be set via the `checkout.fetch` configuration
+variable.
+
 `-f`::
 `--force`::
 	An alias for `--discard-changes`.
diff --git a/builtin/checkout.c b/builtin/checkout.c
index e031e61886..8d810fe2fa 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -30,7 +30,9 @@
 #include "repo-settings.h"
 #include "resolve-undo.h"
 #include "revision.h"
+#include "run-command.h"
 #include "setup.h"
+#include "strvec.h"
 #include "submodule.h"
 #include "symlinks.h"
 #include "trace2.h"
@@ -61,6 +63,7 @@ struct checkout_opts {
 	int count_checkout_paths;
 	int overlay_mode;
 	int dwim_new_local_branch;
+	int fetch;
 	int discard_changes;
 	int accept_ref;
 	int accept_pathspec;
@@ -112,6 +115,36 @@ struct branch_info {
 	char *checkout;
 };
 
+static void fetch_remote_for_start_point(const char *arg)
+{
+	const char *slash;
+	char *remote_name;
+	struct remote *remote;
+	struct child_process cmd = CHILD_PROCESS_INIT;
+
+	if (!arg || !*arg)
+		return;
+
+	slash = strchr(arg, '/');
+	if (slash == arg)
+		return;
+	remote_name = slash ? xstrndup(arg, slash - arg) : xstrdup(arg);
+
+	remote = remote_get(remote_name);
+	if (!remote || !remote_is_configured(remote, 1)) {
+		free(remote_name);
+		return;
+	}
+
+	strvec_pushl(&cmd.args, "fetch", remote_name, NULL);
+	if (slash && slash[1])
+		strvec_push(&cmd.args, slash + 1);
+	cmd.git_cmd = 1;
+	free(remote_name);
+	if (run_command(&cmd))
+		die(_("failed to fetch start-point '%s'"), arg);
+}
+
 static void branch_info_release(struct branch_info *info)
 {
 	free(info->name);
@@ -1237,6 +1270,10 @@ static int git_checkout_config(const char *var, const char *value,
 		opts->dwim_new_local_branch = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "checkout.fetch")) {
+		opts->fetch = git_config_bool(var, value);
+		return 0;
+	}
 
 	if (starts_with(var, "submodule."))
 		return git_default_submodule_config(var, value, NULL);
@@ -1942,8 +1979,13 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 			opts->dwim_new_local_branch &&
 			opts->track == BRANCH_TRACK_UNSPECIFIED &&
 			!opts->new_branch;
-		int n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
-					     &new_branch_info, opts, &rev);
+		int n;
+
+		if (opts->fetch)
+			fetch_remote_for_start_point(argv[0]);
+
+		n = parse_branchname_arg(argc, argv, dwim_ok, which_command,
+					 &new_branch_info, opts, &rev);
 		argv += n;
 		argc -= n;
 	} else if (!opts->accept_ref && opts->from_treeish) {
@@ -2052,6 +2094,8 @@ int cmd_checkout(int argc,
 		OPT_BOOL(0, "overlay", &opts.overlay_mode, N_("use overlay mode (default)")),
 		OPT_BOOL(0, "auto-advance", &opts.auto_advance,
 			 N_("auto advance to the next file when selecting hunks interactively")),
+		OPT_BOOL(0, "fetch", &opts.fetch,
+			 N_("fetch from the remote first if <start-point> is a remote-tracking branch")),
 		OPT_END()
 	};
 
@@ -2102,6 +2146,8 @@ int cmd_switch(int argc,
 			 N_("second guess 'git switch <no-such-branch>'")),
 		OPT_BOOL(0, "discard-changes", &opts.discard_changes,
 			 N_("throw away local modifications")),
+		OPT_BOOL(0, "fetch", &opts.fetch,
+			 N_("fetch from the remote first if <start-point> is a remote-tracking branch")),
 		OPT_END()
 	};
 
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 9bcf7c0b40..cf2ceb4052 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -801,4 +801,72 @@ test_expect_success 'tracking info copied with autoSetupMerge=inherit' '
 	test_cmp_config "" --default "" branch.main2.merge
 '
 
+test_expect_success 'setup upstream for --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 --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 --fetch -b local_new fetch_upstream/fetch_new &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_new HEAD
+'
+
+test_expect_success 'checkout --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 --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 --fetch with bare remote name fetches the remote' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_new2 &&
+	test_commit -C fetch_upstream u_new2 &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2 &&
+	git checkout --fetch -b local_from_remote fetch_upstream &&
+	git rev-parse --verify refs/remotes/fetch_upstream/fetch_new2
+'
+
+test_expect_success 'checkout --fetch aborts and does not create branch on fetch failure' '
+	git checkout main &&
+	test_might_fail git branch -D bogus &&
+	test_must_fail git checkout --fetch -b bogus fetch_upstream/does_not_exist &&
+	test_must_fail git rev-parse --verify refs/heads/bogus
+'
+
+test_expect_success 'checkout.fetch=true enables fetching without --fetch' '
+	git checkout main &&
+	git -C fetch_upstream checkout -b fetch_cfg &&
+	test_commit -C fetch_upstream u_cfg &&
+	test_must_fail git rev-parse --verify refs/remotes/fetch_upstream/fetch_cfg &&
+	git -c checkout.fetch=true checkout -b local_cfg fetch_upstream/fetch_cfg &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_cfg HEAD
+'
+
+test_expect_success 'switch --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 --fetch -c local_switch fetch_upstream/fetch_switch &&
+	test_cmp_rev refs/remotes/fetch_upstream/fetch_switch HEAD
+'
+
 test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 2f9a597ec7..dc1d63669f 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -2602,6 +2602,7 @@ test_expect_success 'double dash "git checkout"' '
 	--ignore-other-worktrees Z
 	--recurse-submodules Z
 	--auto-advance Z
+	--fetch Z
 	--progress Z
 	--guess Z
 	--no-guess Z

base-commit: 94f057755b7941b321fd11fec1b2e3ca5313a4e0
-- 
gitgitgadget

^ permalink raw reply related

* Re: Wrong subject line
From: Kristoffer Haugsbakk @ 2026-04-26  7:07 UTC (permalink / raw)
  To: Harald Nordgren; +Cc: git, gitgitgadget
In-Reply-To: <20260425174448.45681-1-haraldnordgren@gmail.com>

On Sat, Apr 25, 2026, at 19:44, Harald Nordgren wrote:
> I know you are not Ben. Forgot to change the subject line from a
> previous message.

You mean I’m not Phillip. ;)

    Subject: Comments on Phillip's review

I don’t understand why you change the email subjects so often. Right now
I had three “Wrong subject line” in my inbox with lost threading
(webmail client) with the only way to distinguish them being that I was
the the CC on this one.

Most of the time whole 100-email threads like patch series never change
the subject. And to me it is easier to keep track of those “RE: [PATCH
v5] florb: drop glorb” than if someone changes the subject to e.g.
“Regarding memory leaks” because someone found a memory leak in a
review. Because that was a reply to an email from two days ago, but I’ve
been a away for a week so I think it’s a new thread about
something else.

That’s just my experience. My amateur webmail setup doesn’t really
matter here since I just dip in/interrupt threads when I feel like it.

^ permalink raw reply

* Re: [PATCH] l10n: it.po: fix italian usage messages alignment
From: Matteo Beniamino @ 2026-04-26  6:47 UTC (permalink / raw)
  To: Jiang Xin, Matteo Beniamino; +Cc: git, Alessandro Menti
In-Reply-To: <CANYiYbE1qTFYLEyhAR3SEyXozFnEMPxyj=3dAh-vmHkpXZnMpw@mail.gmail.com>

Thanks for your answer. The italian repository found in the TEAMS files
hasn't received an update for more than 5 years. I've opened a PR on the
git-l10n repo, but it doesn't pass all the checks due to many obsolete
entries.

Maybe Alessandro can shed some light on the current status of
the italian translation.

Also, notice that when creating a PR the default PR
message tells the user that the correct way to post a patch is via the
mailing list: I assume this behaviour is inherited by the main git repo, 
but still can be a bit misleading.

Thanks again for your work.

Il 26/04/26 05:01, Jiang Xin ha scritto:
> On Thu, Apr 23, 2026 at 2:25 AM Matteo Beniamino
> <beniamino@beniamino.eu> wrote:
>> 
>> Fixed a misalignment in the "usage:" and "   or:" lines in the
>> italian help messages.
>> 
>> Signed-off-by: Matteo Beniamino <beniamino@beniamino.eu> --- po/
>> it.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
>> 
>> diff --git a/po/it.po b/po/it.po index 20fd8bb28d..b5ccd8c731
>> 100644 --- a/po/it.po +++ b/po/it.po @@ -5400,7 +5400,7 @@ msgstr
>> "uso: %s" #: parse-options.c:915 #, c-format msgid "   or: %s" -
>> msgstr "  oppure: %s" +msgstr "  o: %s"
> 
> An example of the original English output for a command with
> multiple usage lines:
> 
> $ LC_ALL=C git checkout -h | head -2 usage: git checkout [<options>]
> <branch> or: git checkout [<options>] [<branch>] -- <file>...
> 
> The current Italian translation does not achieve colon alignment:
> 
> $ LC_ALL=it  git checkout -h | head -2 uso: git checkout [<opzioni>]
> <branch> oppure: git checkout [<opzioni>] [<branch>] -- <file>...
> 
> Your change fixes the alignment issue, producing the following
> output:
> 
> $ LC_ALL=it  git checkout -h | head -2 uso: git checkout [<opzioni>]
> <branch> o: git checkout [<opzioni>] [<branch>] -- <file>...
> 
> Another possible alignment solution for the Italian output would be:
> 
> $ LC_ALL=it  git checkout -h | head -2 uso: git checkout [<opzioni>]
> <branch> oppure: git checkout [<opzioni>] [<branch>] -- <file>...
> 
> Obviously, your solution is better, because for commands with only a 
> single usage line, such as "git clone -h", no leading spaces will be 
> shown — i.e., it displays "uso: ..." instead of "   uso: ...".
> 
> Git localization follows a GitHub-based workflow; please refer to
> the "po/README.md" file for details. This change can be submitted as
> a Pull Request to the "git-l10n/git-po" repository, and will be
> merged once the next Git localization window opens. If you have
> additional changes to the Italian translation, please refer to the
> "po/TEAMS file", contact the Italian localization leader Alessandro
> Menti, and submit a pull request to his repository.
> 
> -- Jiang Xin

^ permalink raw reply

* Re: [PATCH] l10n: it.po: fix italian usage messages alignment
From: Jiang Xin @ 2026-04-26  3:01 UTC (permalink / raw)
  To: Matteo Beniamino; +Cc: git, Alessandro Menti
In-Reply-To: <20260422182516.26667-2-beniamino@beniamino.eu>

On Thu, Apr 23, 2026 at 2:25 AM Matteo Beniamino <beniamino@beniamino.eu> wrote:
>
> Fixed a misalignment in the "usage:" and "   or:" lines in the italian
> help messages.
>
> Signed-off-by: Matteo Beniamino <beniamino@beniamino.eu>
> ---
>  po/it.po | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/po/it.po b/po/it.po
> index 20fd8bb28d..b5ccd8c731 100644
> --- a/po/it.po
> +++ b/po/it.po
> @@ -5400,7 +5400,7 @@ msgstr "uso: %s"
>  #: parse-options.c:915
>  #, c-format
>  msgid "   or: %s"
> -msgstr "  oppure: %s"
> +msgstr "  o: %s"

An example of the original English output for a command with multiple
usage lines:

    $ LC_ALL=C git checkout -h | head -2
    usage: git checkout [<options>] <branch>
       or: git checkout [<options>] [<branch>] -- <file>...

The current Italian translation does not achieve colon alignment:

    $ LC_ALL=it  git checkout -h | head -2
    uso: git checkout [<opzioni>] <branch>
      oppure: git checkout [<opzioni>] [<branch>] -- <file>...

Your change fixes the alignment issue, producing the following output:

    $ LC_ALL=it  git checkout -h | head -2
    uso: git checkout [<opzioni>] <branch>
      o: git checkout [<opzioni>] [<branch>] -- <file>...

Another possible alignment solution for the Italian output would be:

    $ LC_ALL=it  git checkout -h | head -2
       uso: git checkout [<opzioni>] <branch>
    oppure: git checkout [<opzioni>] [<branch>] -- <file>...

Obviously, your solution is better, because for commands with only a
single usage line, such as "git clone -h", no leading spaces will be
shown — i.e., it displays "uso: ..." instead of "   uso: ...".

Git localization follows a GitHub-based workflow; please refer to the
"po/README.md" file for details. This change can be submitted as a
Pull Request to the "git-l10n/git-po" repository, and will be merged
once the next Git localization window opens. If you have additional
changes to the Italian translation, please refer to the "po/TEAMS
file", contact the Italian localization leader Alessandro Menti, and
submit a pull request to his repository.

--
Jiang Xin

^ permalink raw reply

* Re: [PATCH 6/8] ls-files tests: filter `.gitconfig` from `--others` output
From: Junio C Hamano @ 2026-04-26  0:44 UTC (permalink / raw)
  To: Johannes Schindelin via GitGitGadget; +Cc: git, Johannes Schindelin
In-Reply-To: <ef57244778d8f72754801d80a9e7e8ad034cec28.1777042877.git.gitgitgadget@gmail.com>

"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> +# Remove .gitconfig entries from a file in place.  test-lib.sh may
> +# create $HOME/.gitconfig (e.g. to set safe.bareRepository) which
> +# can appear in ls-files or status output.
> +test_filter_gitconfig () {
> +	sed "/\\.gitconfig/d" "$1" >"$1.filtered" &&
> +	mv "$1.filtered" "$1"
> +}

Thanks.

When I suggested the "just use the usual configuration mechanism to
keep the semantics of existing bare repository tests, and that would
be very unintrusive", I didn't think of this fallout from an extra
file getting reported by ls-files, and this helper function does
make sense.  If we were starting from scratch, we probably would
have created $HOME that is separate from the test repository in the
test framework.  The simplest layout would be to emulate a developer
who has repositories under their $HOME, i.e., $TRASH_DIRECTORY stays
to be the $HOME, but the initial test repository would be created as
a directory inside $TRASH_DIRECTORY instead of using the trash
itself, or something like that.  It is all water under the bridge
now, unless somebody wants an opportunity to work on a non-trivial
clean-up.

By the way, when merged to 'seen', with the 'ps/test-set-e-clean'
topic already in 'next', many tests (especially the ones with early
test_done, like p4 tests in my environment where p4 does not exist)
seem to fail with WITH_BREAKING_CHANGES turned on.  I don't have
enough time to be sitting in front of the keyboard to isolate the
cause, but because this series is one of the topics with biggest
impact to the t/ directory that was replaced after the last
successful run, I thought I should mention it.


^ permalink raw reply

* Re: [PATCH v3 0/8] environment: move core config globals into repo_config_values
From: Bello Olamide @ 2026-04-26  0:31 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me
In-Reply-To: <xmqqqzo27fnu.fsf@gitster.g>

On Sun, 26 Apr 2026 at 01:02, Junio C Hamano <gitster@pobox.com> wrote:
>
> Olamide Caleb Bello <belkid98@gmail.com> writes:
>
> > Olamide Caleb Bello (8):
> >   environment: move "trust_ctime" into `struct repo_config_values`
> >   environment: move "check_stat" into `struct repo_config_values`
> >   environment: move `zlib_compression_level` into `struct
> >     repo_config_values`
> >   environment: move "pack_compression_level" into `struct
> >     repo_config_values`
> >   environment: move "precomposed_unicode" into `struct
> >     repo_config_values`
> >   env: move "core_sparse_checkout_cone" into `struct repo_config_values`
> >   env: move "sparse_expect_files_outside_of_patterns" into
> >     `repo_config_values`
> >   env: move "warn_on_object_refname_ambiguity" into `struct
> >     repo_config_values`
>
> Are there differences between the ones that start with "environment:"
> vs the ones with "env:"?
>
Hi Junio,

There isn’t any semantic difference intended between
the "environment:" and "env:" prefixes

I shortened some of them to stay within the recommended subject length,
but on a second thought I agree that consistency is more important here.

I’ll standardize them in the next revision.
Thanks.
> >
> >  builtin/cat-file.c        |  7 ++++---
> >  builtin/fast-import.c     |  8 +++++---
> >  builtin/index-pack.c      |  3 ++-
> >  builtin/mv.c              |  2 +-
> >  builtin/pack-objects.c    | 24 +++++++++++++----------
> >  builtin/sparse-checkout.c | 37 +++++++++++++++++++++---------------
> >  compat/precompose_utf8.c  | 20 +++++++++++++-------
> >  diff.c                    |  3 ++-
> >  dir.c                     |  3 ++-
> >  entry.c                   |  3 ++-
> >  environment.c             | 40 +++++++++++++++++++++------------------
> >  environment.h             | 19 ++++++++++---------
> >  http-push.c               |  3 ++-
> >  object-file.c             |  6 ++++--
> >  object-name.c             |  3 ++-
> >  revision.c                |  7 ++++---
> >  sparse-index.c            |  4 ++--
> >  statinfo.c                | 12 +++++++-----
> >  submodule.c               |  7 ++++---
> >  upload-pack.c             |  3 ++-
> >  20 files changed, 126 insertions(+), 88 deletions(-)

^ 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