Git development
 help / color / mirror / Atom feed
* [PATCH v2] describe: fix --exclude, --match with --contains and --all
From: Jacob Keller @ 2026-06-01 23:36 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Jacob Keller, Tuomas Ahola

From: Jacob Keller <jacob.keller@gmail.com>

git describe --contains acts as a wrapper around git name-rev. When
operating with --contains and --all, the --match and --exclude patterns
are not properly forwarded to name-rev as --exclude and --refs options.

This results in the command silently discarding match and exclude
requests from the user when operating in --all mode.

We could check and die() if the user provides --contains, --all, and
--match/--exclude. However, its also straight forward to just pass the
filters down to git name-rev.

Notice that the documentation for --match and --exclude mention the
--all mode. It explains that they operate on refs with the prefix
refs/tags, and additionally refs/heads and refs/remotes when using
--all.

Fix the describe logic to pass the patterns down with the appropriate
prefixes when --all is provided. This fixes the support to match the
documented behavior.

Add tests to check that this works as expected.

Reported-by: Tuomas Ahola <taahol@utu.fi>
Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
---
Changes in v2:
* Use check_describe for tests.
* Fix test which could return multiple answers by adding an additional
  exclude.
* Add a few additional tests around origin remote.

 builtin/describe.c  | 18 +++++++++++++++---
 t/t6120-describe.sh | 22 ++++++++++++++++++++++
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/builtin/describe.c b/builtin/describe.c
index 1c47d7c0b7c3..faaf44cec573 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -712,13 +712,25 @@ int cmd_describe(int argc,
 			     NULL);
 		if (always)
 			strvec_push(&args, "--always");
-		if (!all) {
+		if (!all)
 			strvec_push(&args, "--tags");
+
+		for_each_string_list_item(item, &patterns)
+			strvec_pushf(&args, "--refs=refs/tags/%s", item->string);
+		for_each_string_list_item(item, &exclude_patterns)
+			strvec_pushf(&args, "--exclude=refs/tags/%s", item->string);
+
+		if (all) {
 			for_each_string_list_item(item, &patterns)
-				strvec_pushf(&args, "--refs=refs/tags/%s", item->string);
+				strvec_pushf(&args, "--refs=refs/heads/%s", item->string);
 			for_each_string_list_item(item, &exclude_patterns)
-				strvec_pushf(&args, "--exclude=refs/tags/%s", item->string);
+				strvec_pushf(&args, "--exclude=refs/heads/%s", item->string);
+			for_each_string_list_item(item, &patterns)
+				strvec_pushf(&args, "--refs=refs/remotes/%s", item->string);
+			for_each_string_list_item(item, &exclude_patterns)
+				strvec_pushf(&args, "--exclude=refs/remotes/%s", item->string);
 		}
+
 		if (argc)
 			strvec_pushv(&args, argv);
 		else
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 8ee3d2c37d02..4d72033e391d 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -359,6 +359,28 @@ test_expect_success 'describe --contains and --no-match' '
 	test_cmp expect actual
 '
 
+test_expect_success 'describe --contains --all --match no matching commit' '
+	echo "tags/A^0" >expect &&
+	tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+	test_must_fail git describe --contains --all --match="B" $tagged_commit
+'
+
+check_describe "tags/A^0" --contains --all --match="A" $(git rev-parse "refs/tags/A^0")
+
+check_describe "branch_A" --contains --all --match="branch*" $(git rev-parse "refs/tags/A^0")
+
+check_describe "branch_C~1" --contains --all --match="branch*" --exclude="branch_A" $(git rev-parse "refs/tags/A^0")
+
+check_describe "branch_A" --contains --all \
+	--exclude="A" --exclude="c" --exclude="test*" --exclude="origin/remote_branch_A" \
+	$(git rev-parse "refs/tags/A^0")
+
+check_describe "remotes/origin/remote_branch_A" --contains --all --match="origin/remote*" $(git rev-parse "refs/tags/A^0")
+
+check_describe "remotes/origin/remote_branch_C~1" --contains --all \
+	--match="origin/remote*" --exclude="origin/remote_branch_A" \
+	$(git rev-parse "refs/tags/A^0")
+
 test_expect_success 'setup and absorb a submodule' '
 	test_create_repo sub1 &&
 	test_commit -C sub1 initial &&
-- 
2.54.0.633.g0ded84c31b89


^ permalink raw reply related

* Re: [PATCH 2/2] builtin/history: implement "drop" subcommand
From: Junio C Hamano @ 2026-06-01 23:43 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <20260601-b4-pks-history-drop-v1-2-643e32340d55@pks.im>

Patrick Steinhardt <ps@pks.im> writes:

> A common operation when editing the commit history is to drop a specific
> commit from the history entirely, but this operation is not currently
> covered by git-history(1).
>
> A couple of noteworthy bits:
>
>   - This is the first git-history(1) command that will ultimately result
>     in changes to both the index and the working tree. We thus have to
>     add logic to merge resulting changes into those.
>
>   - It is still not possible to replay merge commits, so this limitation
>     is inherited for the new "drop" command.
>
>   - For now we refuse to drop root commits. While we _can_ indeed drop
>     root commits in the general case, there are edge cases where the
>     resulting history would become completely empty. This is thus left
>     to a subsequent patch series.
>
> Other than that, most of the logic is rather straight-forward as we can
> continue to build on the preexisting logic in git-history(1) for most of
> the part.
>
> Signed-off-by: Patrick Steinhardt <ps@pks.im>
> ---
> ...
> +static int update_worktree(struct repository *repo,
> +			   const struct commit *old_head,
> +			   const struct commit *new_head,
> +			   bool dry_run)
> +{
> +...
> +
> +out:
> +	clear_unpack_trees_porcelain(&opts);
> +	rollback_lock_file(&lock);
> +	release_index(&index);
> +	free(desc_buf[0]);
> +	free(desc_buf[1]);
> +	return ret;
> +}

The function looks very familiar---anybody who wants to perform
"checkout <other-commit>" needs to do exactly the above.  It is a
bit surprising and disappointing that this topic needs to *invent*
its own helper function and carry it as a file-scope static.

> +	if (head_moves && update_worktree(repo, old_head, new_head, false) < 0) {
> +		ret = error(_("failed to update working tree; "
> +			      "run `git checkout HEAD` to sync"));
> +		goto out;
> +	}

This is minor, but unlike in documentation pages written in AsciiDoc, we do
not do backticks for literals in our error messages, I think.

^ permalink raw reply

* Re: [PATCH v4] config: improve diagnostic for "set" with missing value
From: Junio C Hamano @ 2026-06-01 23:45 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget
  Cc: git, Kristoffer Haugsbakk, Harald Nordgren
In-Reply-To: <pull.2302.v4.git.git.1779823288005.gitgitgadget@gmail.com>

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

> +static int is_valid_key(const char *key)
> +{
> +	const char *last_dot = strrchr(key, '.');
> +
> +	return last_dot && isalpha(last_dot[1]);
> +}

None of these are valid configuration variable names, but this
function would allow any of them, no?

    1foo.bar
    1foo.some.bar
    foo.b_r
    foo.some.b_r

or does the caller reject such "key" before calling us?

> +static NORETURN void die_missing_set_value(const char *arg)
> +{
> +	const char *last_dot = strrchr(arg, '.');
> +	const char *eq = last_dot ? strchr(last_dot + 1, '=') : NULL;

OK, the intention is to see "foo.bar=baz" and guess that assinging
to "foo.bar" might be what the user wanted.  eq here would point at
that '='.  And ...

> +	char *prefix = eq ? xstrndup(arg, eq - arg) : NULL;

... prefix is our own copy of "foo.bar".

> +	if (prefix && is_valid_key(prefix)) {
> +		error(_("missing value to set to the variable '%s'"), arg);
> +		advise(_("did you mean \"git config set %s %s\"?"),
> +		       prefix, eq + 1);

OK.  If is_valid_key() rejected invalid variable names correctly,
this would catch $A=$B where $A is a plausible-looking name.

> +	} else if (is_valid_key(arg)) {
> +		error(_("missing value to set to the variable '%s'"), arg);
> +	} else {
> +		error(_("missing value to set to a variable with an invalid name '%s'"),
> +		      arg);
> +	}

The distinction among these three messages does look reasonable,
provided if is_valid_key() gives the correct result.

I wonder if it is too hard to refactor existing logic (perhaps it is
used in git_config_parse_key(), no?) to give us a less noisy version
of it that we can use as is_valid_key() here?

Other than that, the remainder of the code changes looked reasonable
to me.  Thanks.

^ permalink raw reply

* Re: [PATCH v4] config: improve diagnostic for "set" with missing value
From: Junio C Hamano @ 2026-06-01 23:53 UTC (permalink / raw)
  To: Harald Nordgren via GitGitGadget
  Cc: git, Kristoffer Haugsbakk, Harald Nordgren
In-Reply-To: <pull.2302.v4.git.git.1779823288005.gitgitgadget@gmail.com>

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

> +static int is_valid_key(const char *key)
> +{
> +	const char *last_dot = strrchr(key, '.');
> +
> +	return last_dot && isalpha(last_dot[1]);
> +}

None of these are valid configuration variable names, but this
function would allow any of them, no?

    1foo.bar
    1foo.some.bar
    foo.b_r
    foo.some.b_r

or does the caller reject such "key" before calling us?

> +static NORETURN void die_missing_set_value(const char *arg)
> +{
> +	const char *last_dot = strrchr(arg, '.');
> +	const char *eq = last_dot ? strchr(last_dot + 1, '=') : NULL;

OK, the intention is to see "foo.bar=baz" and guess that assinging
to "foo.bar" might be what the user wanted.  eq here would point at
that '='.  And ...

> +	char *prefix = eq ? xstrndup(arg, eq - arg) : NULL;

... prefix is our own copy of "foo.bar".

> +	if (prefix && is_valid_key(prefix)) {
> +		error(_("missing value to set to the variable '%s'"), arg);
> +		advise(_("did you mean \"git config set %s %s\"?"),
> +		       prefix, eq + 1);

OK.  If is_valid_key() rejected invalid variable names correctly,
this would catch $A=$B where $A is a plausible-looking name.

> +	} else if (is_valid_key(arg)) {
> +		error(_("missing value to set to the variable '%s'"), arg);
> +	} else {
> +		error(_("missing value to set to a variable with an invalid name '%s'"),
> +		      arg);
> +	}

The distinction among these three messages does look reasonable,
provided if is_valid_key() gives the correct result.

I wonder if it is too hard to refactor existing logic (perhaps it is
used in git_config_parse_key(), no?) to give us a less noisy version
of it that we can use as is_valid_key() here?

Other than that, the remainder of the code changes looked reasonable
to me.  Thanks.

^ permalink raw reply

* Re: [PATCH v4 5/8] environment: move "precomposed_unicode" into `struct repo_config_values`
From: Junio C Hamano @ 2026-06-01 23:54 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me
In-Reply-To: <20260601154211.82370-6-belkid98@gmail.com>

Olamide Caleb Bello <belkid98@gmail.com> writes:

> The `core.precomposeunicode` configuration is currently stored in the
> global variable `precomposed_unicode`, which makes it shared across
> repository instances within a single process.
> ...
> Change the type of the field from `int` to `bool` since it is parsed
> as a boolean value.

Is it really?  The variable (or the structure member in the new
code) needs to be initialized to -1, so in that sense it is tristate
(unspecified -1, false 0, or true 1).

> diff --git a/environment.h b/environment.h
> index 514576b67a..508cb1afbc 100644
> --- a/environment.h
> +++ b/environment.h
> @@ -95,6 +95,7 @@ struct repo_config_values {
>  	int check_stat;
>  	int zlib_compression_level;
>  	int pack_compression_level;
> +	int precomposed_unicode;

And the code does not make such a type change.  Leaving it "int" is
also the right thing to do for this topic, as its stated goal is to
turn the process-wide global into a per-repository setting.

^ permalink raw reply

* Re: [PATCH v4 8/8] environment: move "warn_on_object_refname_ambiguity" into `struct repo_config_values`
From: Junio C Hamano @ 2026-06-02  0:05 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me
In-Reply-To: <20260601154211.82370-9-belkid98@gmail.com>

Olamide Caleb Bello <belkid98@gmail.com> writes:

> @@ -684,11 +684,12 @@ static int get_oid_basic(struct repository *r, const char *str, int len,
>  	int refs_found = 0;
>  	int at, reflog_len, nth_prior = 0;
>  	int fatal = !(flags & GET_OID_QUIETLY);
> +	struct repo_config_values *cfg = repo_config_values(the_repository);

The theme of this topic, however, is to turn the process-wide global
into per-repository setting, so it may appear to be a bit unrelated
change, but the function already takes a repository instance, which
may be different from the_repository.  In the longer run, we
definitely want to see this call pass 'r' instead of
'the_repository', after making sure that repo-config-values for
repository 'r' has already been properly initialized in the program
flow that leads here.

If we want to be conservative, keep the call passing the_repository,
but leave an in-code comment 

	/*
	 * NEEDSWORK: pass 'r' instead of the_repository after
	 * making sure that repo_config_values for 'r' does have
	 * the right value for the repository.
	 */

or something like that nearby.

> diff --git a/submodule.c b/submodule.c
> index b1a0363f9d..f26235bbb7 100644
> --- a/submodule.c
> +++ b/submodule.c
> @@ -898,12 +898,13 @@ static void collect_changed_submodules(struct repository *r,
>  	struct setup_revision_opt s_r_opt = {
>  		.assume_dashdash = 1,
>  	};
> +	struct repo_config_values *cfg = repo_config_values(the_repository);

Likewise.

There may be other places with the same issue I may have missed.



^ permalink raw reply

* Re: [PATCH v4 3/8] environment: move `zlib_compression_level` into `struct repo_config_values`
From: Junio C Hamano @ 2026-06-02  0:07 UTC (permalink / raw)
  To: Olamide Caleb Bello
  Cc: git, phillip.wood123, christian.couder, usmanakinyemi202,
	kaartic.sivaraam, me
In-Reply-To: <20260601154211.82370-4-belkid98@gmail.com>

Olamide Caleb Bello <belkid98@gmail.com> writes:

> @@ -906,6 +906,7 @@ static int start_loose_object_common(struct odb_source *source,
>  	const struct git_hash_algo *algo = source->odb->repo->hash_algo;
>  	const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
>  	int fd;
> +	struct repo_config_values *cfg = repo_config_values(the_repository);

Would source->odb->repo have properly initialized repo_config_values
structure at this point?  Shouldn't we be using it for this call,
instead of the_repository?

>  	fd = create_tmpfile(source->odb->repo, tmp_file, filename);
>  	if (fd < 0) {
> @@ -921,7 +922,7 @@ static int start_loose_object_common(struct odb_source *source,
>  	}
>  
>  	/*  Setup zlib stream for compression */
> -	git_deflate_init(stream, zlib_compression_level);
> +	git_deflate_init(stream, cfg->zlib_compression_level);
>  	stream->next_out = buf;
>  	stream->avail_out = buflen;
>  	algo->init_fn(c);

^ permalink raw reply

* Re: [PATCH] describe: fix --exclude, --match with --contains and --all
From: Junio C Hamano @ 2026-06-02  0:10 UTC (permalink / raw)
  To: Jacob Keller; +Cc: Tuomas Ahola, git, Jacob Keller
In-Reply-To: <3ad3a7ad-14de-4972-acbd-433ad4ced7f8@intel.com>

Jacob Keller <jacob.e.keller@intel.com> writes:

> Ya something like that is probably better. I'll look at cooking up a v2
> which improves the test here. I think part of the issue is that the
> previous tests setup a bunch of tags and branches, so figuring out what
> all the possible outputs are is tricky. Probably I can just add
> additional excludes until there is only one answer.

That sounds workable.  Thanks.

^ permalink raw reply

* Re: [PATCH] doc: document and test `@` prefix for raw timestamps
From: Junio C Hamano @ 2026-06-02  0:23 UTC (permalink / raw)
  To: Luna Schwalbe; +Cc: git
In-Reply-To: <20260601213944.645731-2-dev@luna.gl>

Luna Schwalbe <dev@luna.gl> writes:

> diff --git a/Documentation/date-formats.adoc b/Documentation/date-formats.adoc
> index e24517c49..83f676585 100644
> --- a/Documentation/date-formats.adoc
> +++ b/Documentation/date-formats.adoc
> @@ -10,6 +10,12 @@ Git internal format::
>  	`<time-zone-offset>` is a positive or negative offset from UTC.
>  	For example CET (which is 1 hour ahead of UTC) is `+0100`.
>  
> +    It is safer to prepend the `<unix-timestamp>` with `@`
> +    (e.g., `@0 +0000`), which forces Git to interpret it as a raw
> +    timestamp. This is required for values less than 100,000,000
> +    (which have fewer than 9 digits) to avoid confusion with other
> +    date formats (like `YYYYMMDD`).

Does this "additional paragraph" format correctly, instead of
rendered as a literal block (typically typeset in typewriter font,
monospace)?  Don't you need to do something like what is done for
"ISO 8601::" that appears later in the same file?  I.e. lose the
four-space indent and replace the blank line before it with a single
'+' list continuation operator?

> +# pathologically small timestamps requiring `@` prefix
> +check_parse '@0 +0000' '1970-01-01 00:00:00 +0000'
> +check_parse '@99999999 +0000' '1973-03-03 09:46:39 +0000'
> +check_parse '99999999 +0000' bad

This is totally outside the scope of this topic, but we might want
to enhance the rule a bit to declare this is *not* ambigous.  As
there is no 99th month or 99th day, this cannot be in the YYYYMMDD
date format.

> +check_parse '@100000000 +0000' '1973-03-03 09:46:40 +0000'
> +check_parse '100000000 +0000' '1973-03-03 09:46:40 +0000'


^ permalink raw reply

* Re: [PATCH] docs: fix typos and grammar
From: Weijie Yuan @ 2026-06-02  5:58 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Andrew Kreimer, git
In-Reply-To: <xmqq8q8x3nox.fsf@gitster.g>

On Tue, Jun 02, 2026 at 07:23:10AM +0900, Junio C Hamano wrote:
> Sorry, I lost track.

You are welcome!

> How does this patch relate to the large patch from Andrew that you
> reviewed earlier?  Is this meant to replace it, or is it an
> independent effort that may or may not overlap what is fixed by the
> other patch?  Something else?

My patch is an addition to Andrew's patch.

I think it's better to keep these "boring" typos thing in a thread,
rather than in a new thread, so I replied in this thread, as it would
be better to avoid cluttering the mailing list archive pages and leave
them as much as possible for genuine technical discussions ;-).

Before Andrew said he was preparing for his v2, I was planning to sort
Andrew's patch out and make it more logical or perhaps split it into
several individual patches. But since Andrew replied he's going to do
his v2, I won't take credit for his work, of course.

> Thanks.  All the changes in _this_ patch looked sensible to me (and
> to my agent as well ;-).

Thank you! Looking forward to making "real" contributions one day :)

^ permalink raw reply

* [PATCH] read_gitfile_gently(): return non-repo path on error
From: Jeff King @ 2026-06-02  6:11 UTC (permalink / raw)
  To: git; +Cc: Tian Yuchen

This patch fixes a potential segfault when resolving a .git file that
points to an invalid path. The bug was introduced by 1dd27bfbfd (setup:
improve error diagnosis for invalid .git files, 2026-03-04).

In setup_git_directory_gently() we call read_gitfile_gently(), which may
return a numeric code to us on error. If die_on_error is set, we then
feed that code to read_gitfile_error_die(), which also wants the path to
the gitfile and, in the case of ERROR_NOT_A_REPO, the non-repo directory
that the gitfile pointed to.

But we don't have that pointed-to directory available, so we just pass
NULL. That ends up calling die("not a git repository: %s", NULL). This
may crash, though on many systems (like glibc) it will just print
"(null)". So even if we don't crash, we're generating nonsense output.

The problem comes from 1dd27bfbfd. Before that, when die_on_error was
set we'd pass NULL to read_gitfile_gently()'s return_error_code
parameter, which means it would call read_gitfile_error_die() itself.
And it _does_ have that pointed-to directory as a string, and correctly
passes it.  But since 1dd27bfbfd, we always get the numeric error code
back from read_gitfile_gently(), and then decide whether to call
read_gitfile_error_die() in the caller. And since we don't have the
"dir" parameter, we just pass NULL.

Unfortunately the fix is not a simple matter of passing the string to
the right function. We have to get it out of read_gitfile_gently() in
the first place, which means we have to return it as another
out-parameter. And because it involves allocating memory, we can't just
do so unconditionally; callers need to be ready to free it after
handling the error.

I've tried to make the minimally-invasive fix here:

  1. We only copy the string when we hit READ_GITFILE_ERR_NOT_A_REPO,
     so other error codes don't have to worry about freeing it.

  2. We'll turn read_gitfile_gently() into a wrapper which passes NULL
     by default, leaving other callers unaffected.

The result is kind of gross. There's an extra layer of macro
indirection, and the validity of the string is subtly tied to the
NOT_A_REPO error. A cleaner solution might be an error struct that
couples the code and the output string together, along with a function
to free the error struct. But then all callers would have to be modified
to call the free function. Alternatively, we could perhaps put a
large-ish fixed-size buffer in the struct, though that means potential
truncation and a larger stack footprint in each caller (even when they
don't have see an error).

So I've left that as possible work for the future, or maybe never. Some
of this gross-ness was already there. For example, the only other caller
of read_gitfile_error_die() is in submodule.c, and it also passes NULL
for the "dir" parameter. But it does so only when the code is not
NOT_A_REPO! So it is depending on the same subtle connection to avoid
triggering the bug.

There's an existing test in t0002 which triggers this case, but we
didn't notice the problem because it checks only that we said "not a
repository", and not the full string. So if we print "(null)" it is
happy. It will probably crash on some non-glibc platforms, but nobody
seems to have reported it yet (the breakage is recent-ish as of v2.54).
I'm also somewhat surprised that building with ASan/UBSan doesn't catch
this, but it doesn't seem to (and I found an open issue with somebody
asking for it to be implemented in the sanitizers).

We can beef up the test by checking for the full string, which does
demonstrate the bug.

Signed-off-by: Jeff King <peff@peff.net>
---
Two other points of interest.

One, I'm not sure how useful printing the pointed-to directory is. We
_could_ just say:

  fatal: gitfile does not point to a valid repository: /path/to/.git

which is enough for somebody to investigate themselves. That would
certainly make the patch smaller.

And two, I ran into this running doc-diff:

  $ ./doc-diff HEAD^ HEAD
  fatal: not a git repository: (null)

The correct output (which this patch produces) is:

  fatal: not a git repository: /home/peff/compile/git/.git/worktrees/worktree3

And indeed, that path is missing. But why? I feel like I've run into
this same problem occasionally over the last year or so, but never
before. Did we get more aggressive about removing worktrees at some
point? I haven't been able to reproduce whatever is killing off the
worktree directory, and by the time I see the error it is long gone.

Anyway, that's not strictly related to this bug, but just how I
happened across it.

 setup.c            | 19 +++++++++++++++----
 setup.h            |  3 ++-
 t/t0002-gitfile.sh |  2 +-
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/setup.c b/setup.c
index 075bf89fa9..2df6fbf595 100644
--- a/setup.c
+++ b/setup.c
@@ -955,8 +955,14 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir)
  * will be set to an error code and NULL will be returned. If
  * return_error_code is NULL the function will die instead (for most
  * cases).
+ *
+ * If the code is READ_GITFILE_ERR_NOT_A_REPO and return_error_dir is
+ * non-NULL, the directory to which the gitfile points will be returned
+ * there. The caller is responsible for freeing the resulting string.
  */
-const char *read_gitfile_gently(const char *path, int *return_error_code)
+const char *read_gitfile_gently_with_error_dir(const char *path,
+					       int *return_error_code,
+					       char **return_error_dir)
 {
 	const int max_file_size = 1 << 20;  /* 1MB */
 	int error_code = 0;
@@ -1021,6 +1027,8 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
 	}
 	if (!is_git_directory(dir)) {
 		error_code = READ_GITFILE_ERR_NOT_A_REPO;
+		if (return_error_dir)
+			*return_error_dir = xstrdup(dir);
 		goto cleanup_return;
 	}
 
@@ -1613,11 +1621,12 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
 		int offset = dir->len, error_code = 0;
 		char *gitdir_path = NULL;
 		char *gitfile = NULL;
+		char *error_dst = NULL;
 
 		if (offset > min_offset)
 			strbuf_addch(dir, '/');
 		strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
-		gitdirenv = read_gitfile_gently(dir->buf, &error_code);
+		gitdirenv = read_gitfile_gently_with_error_dir(dir->buf, &error_code, &error_dst);
 		if (!gitdirenv) {
 			switch (error_code) {
 			case READ_GITFILE_ERR_MISSING:
@@ -1641,9 +1650,11 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
 					return GIT_DIR_INVALID_GITFILE;
 			default:
 				if (die_on_error)
-					read_gitfile_error_die(error_code, dir->buf, NULL);
-				else
+					read_gitfile_error_die(error_code, dir->buf, error_dst);
+				else {
+					free(error_dst);
 					return GIT_DIR_INVALID_GITFILE;
+				}
 			}
 		} else {
 			gitfile = xstrdup(dir->buf);
diff --git a/setup.h b/setup.h
index 7878c9d267..65f55d5268 100644
--- a/setup.h
+++ b/setup.h
@@ -39,7 +39,8 @@ int is_nonbare_repository_dir(struct strbuf *path);
 #define READ_GITFILE_ERR_MISSING 9
 #define READ_GITFILE_ERR_IS_A_DIR 10
 void read_gitfile_error_die(int error_code, const char *path, const char *dir);
-const char *read_gitfile_gently(const char *path, int *return_error_code);
+const char *read_gitfile_gently_with_error_dir(const char *path, int *return_error_code, char **return_error_dir);
+#define read_gitfile_gently(path, err) read_gitfile_gently_with_error_dir((path), (err), NULL)
 #define read_gitfile(path) read_gitfile_gently((path), NULL)
 const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
 #define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index dfbcdddbcc..6967e12b9f 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -27,7 +27,7 @@ test_expect_success 'bad setup: invalid .git file format' '
 test_expect_success 'bad setup: invalid .git file path' '
 	echo "gitdir: $REAL.not" >.git &&
 	test_must_fail git rev-parse 2>.err &&
-	test_grep "not a git repository" .err
+	test_grep "not a git repository: $REAL.not" .err
 '
 
 test_expect_success 'final setup + check rev-parse --git-dir' '
-- 
2.54.0.682.g2f9b59d445

^ permalink raw reply related

* Re: [PATCH] docs: fix typos and grammar
From: Weijie Yuan @ 2026-06-02  6:12 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Andrew Kreimer, git
In-Reply-To: <ah5xFk36HZGWdCND@wyuan.org>

On Tue, Jun 02, 2026 at 01:59:01PM +0800, Weijie Yuan wrote:
> > How does this patch relate to the large patch from Andrew that you
> > reviewed earlier?  Is this meant to replace it, or is it an
> > independent effort that may or may not overlap what is fixed by the
> > other patch?  Something else?
>
> My patch is an addition to Andrew's patch.

Sorry for not making my point clear all at once.

My patch is an independent effort and does not overlap with Andrew's,
i.e. doesn't touch the same areas as his.

And I just checked the recent archives, my patch doesn't overlap with
others as well :-)

^ permalink raw reply

* Re: [PATCH] doc: document and test `@` prefix for raw timestamps
From: Jeff King @ 2026-06-02  6:17 UTC (permalink / raw)
  To: Luna Schwalbe; +Cc: git, Junio C Hamano
In-Reply-To: <xmqqfr35zt6h.fsf@gitster.g>

On Tue, Jun 02, 2026 at 09:23:34AM +0900, Junio C Hamano wrote:

> > +    It is safer to prepend the `<unix-timestamp>` with `@`
> > +    (e.g., `@0 +0000`), which forces Git to interpret it as a raw
> > +    timestamp. This is required for values less than 100,000,000
> > +    (which have fewer than 9 digits) to avoid confusion with other
> > +    date formats (like `YYYYMMDD`).
> 
> Does this "additional paragraph" format correctly, instead of
> rendered as a literal block (typically typeset in typewriter font,
> monospace)?  Don't you need to do something like what is done for
> "ISO 8601::" that appears later in the same file?  I.e. lose the
> four-space indent and replace the blank line before it with a single
> '+' list continuation operator?

Yes, I think so. As a tip for contributors, running:

  cd Documentation
  ./doc-diff HEAD^ HEAD

is often good for seeing a rough approximation of the rendered doc. It
shows here that the result is incorrectly indented versus the rest of
the section.

Sadly it is somewhat limited in terms of typography, since it is diffing
the roff-rendered manpages. So you wouldn't realize that it is rendered
in a typewriter font, as you would if you looked at the html output.
Spot-checking the html is also a good thing to do when writing doc
patches.

-Peff

^ permalink raw reply

* Re: [PATCH v2 0/2] commit: remove deprecated functions
From: Jeff King @ 2026-06-02  6:23 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: kristofferhaugsbakk, Kristoffer Haugsbakk, Patrick Steinhardt,
	git
In-Reply-To: <xmqqa4te91g7.fsf@gitster.g>

On Mon, Jun 01, 2026 at 04:14:48PM +0900, Junio C Hamano wrote:

> > This looks obviously correct to me, but the whole topic made me wonder:
> > was it worth retaining the old names and deprecating them, versus just
> > removing them back then?
> >
> > Topics in flight would have needed an update then, but they did
> > eventually anyway. So it feels like the total amount of work done is
> > larger, compared to just fixing them as the topics were merged. Either
> > way the compiler tells us, and the adjustments themselves are small.
> 
> Your alternative approach will depend on the integrator doing all
> the fixups at the merge time.
> 
> The amount of effort required by the entire community as a whole may
> have been larger, but the way the rename was carried out did spread
> them thinner.

True, though my thinking was two-fold:

  - Topics in flight that you _haven't_ picked up yet are not your
    problem. They become the problem of their authors, as long as they
    build on top of the change in question (either originally, or via
    rebase).

  - It's also work to pick up the new topic. So there's some tradeoff
    for the maintainer in how many fixups (and how much effort for each
    one) versus the work to juggle a new topic.

> Admittedly, with help from rerere and merge-fix mechanism, such a
> "fixup at the merge time" typically needs to be done only once per
> the other conflicting topic in flight, but still, when constructing
> a workflow, I try to avoid having to depend on the single bottleneck
> for a task that does not need to be performed by the single
> bottleneck, especially when the single bottleneck has other tasks
> that can only be done by the single bottleneck.

Yeah, I think that is a good philosophy in general. I just wondered
whether the tradeoff was right here (but I'm happy to defer to you for
the final call on that).

-Peff

^ permalink raw reply

* Re: [PATCH v3 0/2] http: fix memory leak in fetch_and_setup_pack_index()
From: Jeff King @ 2026-06-02  6:24 UTC (permalink / raw)
  To: LorenzoPegorari; +Cc: git, Taylor Blau, Junio C Hamano, Patrick Steinhardt, fox
In-Reply-To: <cover.1780321770.git.lorenzo.pegorari2002@gmail.com>

On Mon, Jun 01, 2026 at 03:51:43PM +0200, LorenzoPegorari wrote:

> Patch series that does some cleanup and fixes a memory leak present
> inside the function `fetch_and_setup_pack_index()`.

Thanks, this version looks great to me.

-Peff

^ permalink raw reply

* Re: [PATCH v2] index-pack: retain child bases in delta cache
From: Jeff King @ 2026-06-02  6:45 UTC (permalink / raw)
  To: Arijit Banerjee via GitGitGadget
  Cc: git, Ævar Arnfjörð Bjarmason, Junio C Hamano,
	Derrick Stolee, Arijit Banerjee, Arijit Banerjee
In-Reply-To: <pull.2131.v2.git.1780330402264.gitgitgadget@gmail.com>

On Mon, Jun 01, 2026 at 04:13:21PM +0000, Arijit Banerjee via GitGitGadget wrote:

> When resolving a delta whose result has children of its own,
> index-pack adds the result to work_head, accounts its data in
> base_cache_used, and calls prune_base_data(). It then immediately frees
> that same data.
> 
> This bypasses the existing delta base cache policy and can force later
> descendants to reconstruct the queued base again. Let the existing
> delta_base_cache_limit pruning policy decide whether to keep or evict
> the data instead.
> 
> This does not add a new cache or increase the cache limit. The object
> data is already accounted in base_cache_used before prune_base_data()
> runs, and the existing pruning and base cleanup paths still release it.

That explanation makes sense, but I'm left with one question/concern.
Dropping the data for a base makes sense when we are "done" with it,
because we know we won't need it anymore and it leaves more room in the
cache for things we do care about.

The problem here is that the current notion of "done" is not correct.
Imagine we have delta chains "A -> B -> C" and "A -> D -> F". We are
totally done with A when we have resolved both B and D, but if I
understand correctly, we currently throw it away after just resolving B.

Your patch never throws it away, and just waits for it to get evicted
from the cache due to memory pressure. But could we realize the moment
when B and D have both finished using it, and evict it then? That makes
it more likely for us to keep something useful in the cache when there
is pressure.

I'm not sure how hard that would be in practice, or how much it would
help (the base cache works in list order, so I think it might naturally
be a sort of LRU?).

-Peff

^ permalink raw reply

* Re: [PATCH 2/2] builtin/init-db: deprecate alias for git-init(1)
From: Patrick Steinhardt @ 2026-06-02  6:45 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Kristoffer Haugsbakk, Phillip Wood, git
In-Reply-To: <xmqqcxy93nph.fsf@gitster.g>

On Tue, Jun 02, 2026 at 07:22:50AM +0900, Junio C Hamano wrote:
> "Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com> writes:
> 
> >> I found it to be a bit heavy-handed as it's so trivial to replace with
> >> git-init(1), but on the other hand it's a trivial thing to do.
> >
> > I imagine that most potential git-init-db(1) uses will be buried in some
> > scripts that haven’t been touched in years. Then the Git init might
> > fail, you get errors about git-commit(1) or something not being a thing
> > you can run without a repository, and it ends up being a headscratcher
> > since the original failure gets lost.
> >
> > All to say I think a simple warning would be nice. ;)
> 
> Or just leave it without deprecation.  It does not cost much to keep
> "init-db", and because we expanded what "git database" means in
> later versions of Git since its invention, the name still makes
> sense.  Thank Linus for not naming it "init-odb"---that might have
> been a valid excuse to rename it because it does not cover the ref
> database and config database and others.

I wouldn't mind that outcome much, either. What triggered this series is
that I'm always annoyed that it's "builtin/init-db.c" instead of
"builtin/init.c", and the same for `cmd_init_db()`. But I intentionally
constructed the series in a way that the first commit can be picked
as-is, so that we can adjust our code to the modern world while not
doing the deprecation dance.

So I'd be equally happy if we just drop the second commit in this
series.

Patrick

^ permalink raw reply

* Re: [PATCH 2/2] builtin/history: implement "drop" subcommand
From: Pablo Sabater @ 2026-06-02  7:31 UTC (permalink / raw)
  To: Patrick Steinhardt; +Cc: git
In-Reply-To: <20260601-b4-pks-history-drop-v1-2-643e32340d55@pks.im>

Hi!

El mar, 2 jun 2026 a las 8:16, Patrick Steinhardt (<ps@pks.im>) escribió:

> +
> +static int cmd_history_drop(int argc,
> +                           const char **argv,
> +                           const char *prefix,
> +                           struct repository *repo)
> +{
> +       const char * const usage[] = {
> +               GIT_HISTORY_DROP_USAGE,
> +               NULL,
> +       };
> +       enum replay_empty_commit_action empty = REPLAY_EMPTY_COMMIT_DROP;
> +       enum ref_action action = REF_ACTION_DEFAULT;
> +       int dry_run = 0;
> +       struct option options[] = {
> +               OPT_CALLBACK_F(0, "update-refs", &action, "(branches|head)",
> +                              N_("control which refs should be updated"),
> +                              PARSE_OPT_NONEG, parse_ref_action),
> +               OPT_BOOL('n', "dry-run", &dry_run,
> +                        N_("perform a dry-run without updating any refs")),
> +               OPT_CALLBACK_F(0, "empty", &empty, "(drop|keep|abort)",
> +                              N_("how to handle descendants that become empty"),
> +                              PARSE_OPT_NONEG, parse_opt_empty),
> +               OPT_END(),
> +       };
> +       struct strbuf reflog_msg = STRBUF_INIT;
> +       struct commit *original, *rewritten;
> +       struct rev_info revs = { 0 };
> +       struct replay_result result = { 0 };
> +       struct commit *old_head, *new_head;
> +       bool head_moves = false;
> +       int ret;
> +
> +       argc = parse_options(argc, argv, prefix, options, usage, 0);
> +       if (argc != 1) {
> +               ret = error(_("command expects a single revision"));
> +               goto out;
> +       }
> +       repo_config(repo, git_default_config, NULL);
> +
> +       if (action == REF_ACTION_DEFAULT)
> +               action = REF_ACTION_BRANCHES;
> +
> +       original = lookup_commit_reference_by_name(argv[0]);
> +       if (!original) {
> +               ret = error(_("commit cannot be found: %s"), argv[0]);
> +               goto out;
> +       }
> +
> +       if (!original->parents) {
> +               ret = error(_("cannot drop root commit %s: "
> +                             "it has no parent to replay onto"),
> +                           argv[0]);
> +               goto out;
> +       } else if (original->parents->next) {
> +               ret = error(_("cannot drop merge commit"));

Why the if block adds which commit context, but not on the else if block?

> +               goto out;
> +       }

> diff --git a/t/t3454-history-drop.sh b/t/t3454-history-drop.sh
> new file mode 100755
> index 0000000000..b320ff09b3
> --- /dev/null
> +++ b/t/t3454-history-drop.sh
> @@ -0,0 +1,513 @@
> +#!/bin/sh
> +
> +test_description='tests for git-history drop subcommand'
> +
> +. ./test-lib.sh
> +. "$TEST_DIRECTORY/lib-log-graph.sh"
> +
> +expect_graph () {
> +       cat >expect &&
> +       lib_test_cmp_graph --graph --format=%s "$@"
> +}

This function appears exactly the same at t6016 and t4215 but named as
check_graph. I was gonna do a cleanup for a commit series I'm working
on to bring that function to `lib-log-graph.sh` because all these test
files share that they import graph functions from `lib-log-graph.c`,
maybe you could do it?

Also:

lib_test_cmp_graph () {
        git log --graph "$@" >output &&
        sed 's/ *$//' >output.sanitized <output &&
        test_cmp expect output.sanitized
}

Already uses `--graph` you can drop it from expect_graph()

I can't say much more, from what I tested it worked fine but I haven't
tested very exhaustively tho,

--
Pablo

^ permalink raw reply

* [PATCH v3] config.mak.uname: avoid macOS linker warning on Xcode 16.3+
From: Harald Nordgren via GitGitGadget @ 2026-06-02  7:37 UTC (permalink / raw)
  To: git; +Cc: Harald Nordgren, Harald Nordgren
In-Reply-To: <pull.2313.v2.git.git.1780065163866.gitgitgadget@gmail.com>

From: Harald Nordgren <haraldnordgren@gmail.com>

Building on macOS with Xcode 16.3 or newer emits:

    ld: warning: reducing alignment of section __DATA,__common
    from 0x8000 to 0x4000 because it exceeds segment maximum
    alignment

Pass -fno-common when "ld -v" reports ld-1167 or newer, so tentative
definitions of large arrays go into BSS instead of __DATA,__common.

Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com>
---
    fix macOS linker warning
    
    Check for empty LD_MAJOR_VERSION.

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2313%2FHaraldNordgren%2Fpkt-line-init-buffer-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2313/HaraldNordgren/pkt-line-init-buffer-v3
Pull-Request: https://github.com/git/git/pull/2313

Range-diff vs v2:

 1:  0e660a346e ! 1:  f864912c53 config.mak.uname: avoid macOS linker warning on Xcode 16.3+
     @@ config.mak.uname: ifeq ($(uname_S),Darwin)
       
      +	# Silence Xcode 16.3+ linker warning about __DATA,__common alignment.
      +	LD_MAJOR_VERSION = $(shell ld -v 2>&1 | sed -n 's/.*PROJECT:ld-\([0-9]*\).*/\1/p')
     -+        ifeq ($(shell test "$(LD_MAJOR_VERSION)" -ge 1167 && echo 1),1)
     ++        ifeq ($(shell test -n "$(LD_MAJOR_VERSION)" && test "$(LD_MAJOR_VERSION)" -ge 1167 && echo 1),1)
      +		BASIC_CFLAGS += -fno-common
      +        endif
      +


 config.mak.uname | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/config.mak.uname b/config.mak.uname
index f9a5ad9720..8719e09f66 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -173,6 +173,12 @@ ifeq ($(uname_S),Darwin)
 		NEEDS_GOOD_LIBICONV = UnfortunatelyYes
         endif
 
+	# Silence Xcode 16.3+ linker warning about __DATA,__common alignment.
+	LD_MAJOR_VERSION = $(shell ld -v 2>&1 | sed -n 's/.*PROJECT:ld-\([0-9]*\).*/\1/p')
+        ifeq ($(shell test -n "$(LD_MAJOR_VERSION)" && test "$(LD_MAJOR_VERSION)" -ge 1167 && echo 1),1)
+		BASIC_CFLAGS += -fno-common
+        endif
+
 	# The builtin FSMonitor on MacOS builds upon Simple-IPC.  Both require
 	# Unix domain sockets and PThreads.
         ifndef NO_PTHREADS

base-commit: 1666c1265231b0bc5f613fbbf3f0a9896cdef76e
-- 
gitgitgadget

^ permalink raw reply related

* Re: [PATCH] read_gitfile_gently(): return non-repo path on error
From: Junio C Hamano @ 2026-06-02  7:42 UTC (permalink / raw)
  To: Jeff King; +Cc: git, Tian Yuchen
In-Reply-To: <20260602061159.GA693928@coredump.intra.peff.net>

Jeff King <peff@peff.net> writes:

> I've tried to make the minimally-invasive fix here:
>
>   1. We only copy the string when we hit READ_GITFILE_ERR_NOT_A_REPO,
>      so other error codes don't have to worry about freeing it.
>
>   2. We'll turn read_gitfile_gently() into a wrapper which passes NULL
>      by default, leaving other callers unaffected.

Nice, probably.  I do not know what to feel about the first point,
though, as it burdens those who add new callers in the future more.

> The result is kind of gross. There's an extra layer of macro
> indirection, and the validity of the string is subtly tied to the
> NOT_A_REPO error. A cleaner solution might be an error struct that
> couples the code and the output string together, along with a function
> to free the error struct. But then all callers would have to be modified
> to call the free function. Alternatively, we could perhaps put a
> large-ish fixed-size buffer in the struct, though that means potential
> truncation and a larger stack footprint in each caller (even when they
> don't have see an error).

None of thoese are particularly appetizing ;-).

> So I've left that as possible work for the future, or maybe never. Some
> of this gross-ness was already there. For example, the only other caller
> of read_gitfile_error_die() is in submodule.c, and it also passes NULL
> for the "dir" parameter. But it does so only when the code is not
> NOT_A_REPO! So it is depending on the same subtle connection to avoid
> triggering the bug.

Yup.  I can agree with this.

> ---
> Two other points of interest.
>
> One, I'm not sure how useful printing the pointed-to directory is. We
> _could_ just say:
>
>   fatal: gitfile does not point to a valid repository: /path/to/.git
>
> which is enough for somebody to investigate themselves. That would
> certainly make the patch smaller.

Thanks.  While reading the main explanation, it was the first thing
that came to me.

The implementation and the test are as expected in patches from you
and matches the intent explained in the log message exactly.

Thanks, will queue.

^ permalink raw reply

* Re: [PATCH 2/2] builtin/init-db: deprecate alias for git-init(1)
From: Kristoffer Haugsbakk @ 2026-06-02  7:54 UTC (permalink / raw)
  To: Patrick Steinhardt, Junio C Hamano; +Cc: Phillip Wood, git
In-Reply-To: <ah58IJ8DgSZYRjMM@pks.im>

On Tue, Jun 2, 2026, at 08:45, Patrick Steinhardt wrote:
> On Tue, Jun 02, 2026 at 07:22:50AM +0900, Junio C Hamano wrote:
>> "Kristoffer Haugsbakk" <kristofferhaugsbakk@fastmail.com> writes:
>>>[snip]
>> Or just leave it without deprecation.  It does not cost much to keep
>> "init-db", and because we expanded what "git database" means in
>> later versions of Git since its invention, the name still makes
>> sense.  Thank Linus for not naming it "init-odb"---that might have
>> been a valid excuse to rename it because it does not cover the ref
>> database and config database and others.
>
> I wouldn't mind that outcome much, either. What triggered this series is
> that I'm always annoyed that it's "builtin/init-db.c" instead of
> "builtin/init.c", and the same for `cmd_init_db()`. But I intentionally
> constructed the series in a way that the first commit can be picked
> as-is, so that we can adjust our code to the modern world while not
> doing the deprecation dance.
>
> So I'd be equally happy if we just drop the second commit in this
> series.

Could it be worthwhile to mark it as soft deprecated? In the sense that
it is a legacy alias that is not planned for removal?

What I think was mistake in topic jc/you-still-use-whatchanged was that
git-whatchanged(1) was not explicitly marked as deprecated before that
series, and then it started failing without a new `--i-still-use-this`
flag. The doc before that said:

    New users are encouraged to use git-log(1) instead.  The
    `whatchanged` command is essentially the same as git-log(1) but
    defaults to showing the raw format diff output and skipping merges.

    The command is primarily kept for historical reasons; fingers of
    many people who learned Git long before `git log` was invented by
    reading the Linux kernel mailing list are trained to type it.

Reading between the lines, this looks like a soft deprecation. Then
there were emails that said that there was no prior warning. And then
someone replied to that saying that it had really been deprecated for
over a decade because that was the intent.[1] But IMO just saying
something to the effect of soft deprecated would have been better
(before it got hard deprecated).

Trying to simulate amnesia, I think just the word “init-db” looks
slightly legacy, and the fact that the documentation just links to
git-init(1) solidifies that. On the other hand git-stage(1) was
introduced as a better name for “staging” files and that too just links
to git-add(1). So you have two commands which just link to other
commands, but one is definitely more deprecated than the other.

† 1: But “trained fingers” reading the man page every other year on the
     off chance that there are new developments? That’s another
     question.

^ permalink raw reply

* [BUG] t/perf scripts lose GIT_PERF_* when used with --verbose-log
From: Jeff King @ 2026-06-02  7:56 UTC (permalink / raw)
  To: git

Imagine I have a perf script like this:

  #!/bin/sh
  test_description=foo
  . ./perf-lib.sh
  echo >&2 "large_repo = $GIT_PERF_LARGE_REPO"
  test_perf_large_repo
  [...some actual tests...]

If I run the command below, I'd expect it to use linux.git as the test
repo (and print to stderr telling me so). And it does:

  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./p1234-foo.sh
  large_repo = /path/to/linux.git
  [...]

This is courtesy of 32b74b9809 (perf: do allow `GIT_PERF_*` to be
overridden again, 2025-04-04). But that breaks if we use --tee or any
other option which implies it:

  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./p1234-foo.sh --verbose-log
  large_repo = /home/peff/compile/git/t/..
  [...]

What happens in the happy path is this:

  0. The script sources perf-lib.sh.

  1. perf-lib.sh stashes GIT_PERF_* in a variable to restore later.

  2. perf-lib.sh sources GIT-BUILD-OPTIONS, which overwrites the
     environment.

  3. perf-lib.sh sources test-lib.sh.

  4. test-lib.sh itself sources GIT-BUILD-OPTIONS.

  5. Eventually test-lib.sh finishes, returning control to perf-lib.sh.

  6. perf-lib.sh restores GIT_PERF_* from the stashed copy. All is well.

But if --tee or --verbose-log is used, then step 5 never happens!
Instead test-lib.sh re-executes a second copy of the script piped to
tee. And that re-executed copy sees the environment we had after step 4,
with all of GIT_PERF_* coming from GIT-BUILD-OPTIONS. So even though it
tries to do the save/restore, its step 1 never sees the original
environment (so it "saves" nothing useful).

This is especially insidious if you use the "./run" program to compare
versions. It reads GIT-BUILD-OPTIONS, too, and also knows how to
preserve GIT_PERF_*, courtesy of 79d301c767 (t/perf/run: preserve
GIT_PERF_* from environment, 2026-01-06). But it reads GIT_TEST_OPTS
from the build-options file and runs each script with it. So while this
may work:

  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./run HEAD p1234-foo.sh
  [...]
  === Running 1 tests in /home/peff/compile/git/t/perf/build/1211f0ef99a75931f170bc2a838172a45300ad63/bin-wrappers ===
  large_repo = /path/to/linux.git
  [...]

you may get spooky action at a distance from whenever you last ran make:

  $ make -C ../.. GIT_TEST_OPTS=--verbose-log
  $ GIT_PERF_LARGE_REPO=/path/to/linux.git ./run HEAD p1234-foo.sh
  [...]
  === Running 1 tests in /home/peff/compile/git/t/perf/build/1211f0ef99a75931f170bc2a838172a45300ad63/bin-wrappers ===
  large_repo = /home/peff/compile/git/t/..

Doubly confusing if that GIT_TEST_OPTS is in your config.mak (because
you want normal "make test" to run under prove but still keep logs, and
you put it in the file ages ago).

I don't think this can be fixed by perf-lib.sh. The problem is internal
to test-lib.sh, which is overwriting the environment when it sources
GIT-BUILD-OPTIONS, without any opportunity for perf-lib to act before
getting re-executed. It would require test-lib.sh itself to have some
notion of "here are some stashed variables; restore them via eval".

Which just feels like stacking band-aids upon band-aids. The original
problem started with 4638e8806e (Makefile: use common template for
GIT-BUILD-OPTIONS, 2024-12-06), though one could argue that even before
then the precedence rules were kind of sketchy (it just made things much
worse because now it crops up even if you don't set GIT_PERF_LARGE_REPO
in your config.mak at all).

So I dunno. I couldn't quite bring myself to write a patch, but I
thought I'd at least write a warning to the list in case anybody else is
bit by it.

-Peff

^ permalink raw reply

* Re: [PATCH] read_gitfile_gently(): return non-repo path on error
From: Jeff King @ 2026-06-02  8:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Tian Yuchen
In-Reply-To: <xmqq4ijlz8vc.fsf@gitster.g>

On Tue, Jun 02, 2026 at 04:42:15PM +0900, Junio C Hamano wrote:

> > One, I'm not sure how useful printing the pointed-to directory is. We
> > _could_ just say:
> >
> >   fatal: gitfile does not point to a valid repository: /path/to/.git
> >
> > which is enough for somebody to investigate themselves. That would
> > certainly make the patch smaller.
> 
> Thanks.  While reading the main explanation, it was the first thing
> that came to me.

Here's what that looks like, for reference. It is nice and simple, if we
think the change in error message is acceptable. I hate to change
user-facing error messages because of internal code details, but I
really do wonder if the existing message is the most useful thing to
print in the first place.

diff --git a/setup.c b/setup.c
index 075bf89fa9..ed86671d84 100644
--- a/setup.c
+++ b/setup.c
@@ -920,7 +920,7 @@ int verify_repository_format(const struct repository_format *format,
 	return 0;
 }
 
-void read_gitfile_error_die(int error_code, const char *path, const char *dir)
+void read_gitfile_error_die(int error_code, const char *path)
 {
 	switch (error_code) {
 	case READ_GITFILE_ERR_NOT_A_FILE:
@@ -940,7 +940,8 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir)
 	case READ_GITFILE_ERR_NO_PATH:
 		die(_("no path in gitfile: %s"), path);
 	case READ_GITFILE_ERR_NOT_A_REPO:
-		die(_("not a git repository: %s"), dir);
+		die(_("gitfile does not point to a valid repository: %s"),
+		    path);
 	default:
 		BUG("unknown error code");
 	}
@@ -1031,7 +1032,7 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
 	if (return_error_code)
 		*return_error_code = error_code;
 	else if (error_code)
-		read_gitfile_error_die(error_code, path, dir);
+		read_gitfile_error_die(error_code, path);
 
 	free(buf);
 	return error_code ? NULL : path;
@@ -1641,7 +1642,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
 					return GIT_DIR_INVALID_GITFILE;
 			default:
 				if (die_on_error)
-					read_gitfile_error_die(error_code, dir->buf, NULL);
+					read_gitfile_error_die(error_code, dir->buf);
 				else
 					return GIT_DIR_INVALID_GITFILE;
 			}
diff --git a/setup.h b/setup.h
index 7878c9d267..436aaa22c1 100644
--- a/setup.h
+++ b/setup.h
@@ -38,7 +38,7 @@ int is_nonbare_repository_dir(struct strbuf *path);
 #define READ_GITFILE_ERR_TOO_LARGE 8
 #define READ_GITFILE_ERR_MISSING 9
 #define READ_GITFILE_ERR_IS_A_DIR 10
-void read_gitfile_error_die(int error_code, const char *path, const char *dir);
+void read_gitfile_error_die(int error_code, const char *path);
 const char *read_gitfile_gently(const char *path, int *return_error_code);
 #define read_gitfile(path) read_gitfile_gently((path), NULL)
 const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
diff --git a/submodule.c b/submodule.c
index a939ff5072..c36732ca0b 100644
--- a/submodule.c
+++ b/submodule.c
@@ -2578,7 +2578,7 @@ void absorb_git_dir_into_superproject(const char *path,
 
 		if (err_code != READ_GITFILE_ERR_NOT_A_REPO)
 			/* We don't know what broke here. */
-			read_gitfile_error_die(err_code, path, NULL);
+			read_gitfile_error_die(err_code, path);
 
 		/*
 		* Maybe populated, but no git directory was found?
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
index dfbcdddbcc..6356e9ec72 100755
--- a/t/t0002-gitfile.sh
+++ b/t/t0002-gitfile.sh
@@ -27,7 +27,7 @@ test_expect_success 'bad setup: invalid .git file format' '
 test_expect_success 'bad setup: invalid .git file path' '
 	echo "gitdir: $REAL.not" >.git &&
 	test_must_fail git rev-parse 2>.err &&
-	test_grep "not a git repository" .err
+	test_grep "gitfile does not point to a valid repository" .err
 '
 
 test_expect_success 'final setup + check rev-parse --git-dir' '

^ permalink raw reply related

* Re: [PATCH v4 3/8] environment: move `zlib_compression_level` into `struct repo_config_values`
From: Patrick Steinhardt @ 2026-06-02  8:12 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Olamide Caleb Bello, git, phillip.wood123, christian.couder,
	usmanakinyemi202, kaartic.sivaraam, me
In-Reply-To: <xmqqpl29ztx7.fsf@gitster.g>

On Tue, Jun 02, 2026 at 09:07:32AM +0900, Junio C Hamano wrote:
> Olamide Caleb Bello <belkid98@gmail.com> writes:
> 
> > @@ -906,6 +906,7 @@ static int start_loose_object_common(struct odb_source *source,
> >  	const struct git_hash_algo *algo = source->odb->repo->hash_algo;
> >  	const struct git_hash_algo *compat = source->odb->repo->compat_hash_algo;
> >  	int fd;
> > +	struct repo_config_values *cfg = repo_config_values(the_repository);
> 
> Would source->odb->repo have properly initialized repo_config_values
> structure at this point?  Shouldn't we be using it for this call,
> instead of the_repository?

I think as an intermediate step it's okay-ish to use `the_repository`,
as it doesn't make the status quo any worse. But ideally, we'd have a
follow-up patch series that converts "object-file.c" to drop the
dependency on `the_repository` completely, which will be easier after
this patch series here has landed as there will only be a handful more
config options to migrate:

  - `pack_compression_level` and `zlib_compression_level` get migrated
    in this series.

  - `object_creation_mode` still needs migration.

  - `pack_size_limit_cfg` still needs migration.

Other than that we really only need to use the correct repo in a small
set of functions.

Overall, I think it's sensible to always use `the_repository` at the
callsites in a patch series like this so that it's obvious that there is
no change in behaviour. So every patch series that gets rid of global
state in a subsystem X will basically bubble up the global state into
the next-higher level, and it's then the duty of the next patch series
to address that next-higher level.

The only exception of course is subsystems that already got rid of
`the_repository` -- we really shouldn't reintroduce the use there.

Patrick

^ permalink raw reply

* Re: [PATCH] doc: document and test `@` prefix for raw timestamps
From: Luna Schwalbe @ 2026-06-02  8:15 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git
In-Reply-To: <xmqqfr35zt6h.fsf@gitster.g>

 > Does this "additional paragraph" format correctly, instead of
 > rendered as a literal block (typically typeset in typewriter font,
 > monospace)?  Don't you need to do something like what is done for
 > "ISO 8601::" that appears later in the same file?  I.e. lose the
 > four-space indent and replace the blank line before it with a single
 > '+' list continuation operator?

Terribly sorry, you're right of course, I somehow forgot to actually 
build and check the docs. Will send an updated patch right away.

 > This is totally outside the scope of this topic, but we might want
 > to enhance the rule a bit to declare this is *not* ambigous.  As
 > there is no 99th month or 99th day, this cannot be in the YYYYMMDD
 > date format.

I agree there is room for change with this rule, although I'm not sure 
how sensible it is to start allowing certain values based on whether 
they are also a valid calendar date or not (we'd end up trying to parse 
YYYYMMDD first, and only afterwards do the actual timestamp parsing; I 
feel like this might just make the system less predictable for users in 
practice).

As far as I can tell the rule is technically not necessary at all (apart 
from some unusual approxidate interpretations like the `2000 +0000` 
example, which I honestly think are more confusing than useful), seeing 
that YYYYMMDD isn't a supported format anywhere.

If we want to have it as a safeguard tho, better documentation is 
probably the most important aspect. As a user, ideally I'd love to get a 
"ambiguous date format, prefix with @ if you intend to specify a raw 
timestamp" kind of error message, but I suspect that might be difficult 
to implement.

^ 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