]> git.ipfire.org Git - thirdparty/git.git/log
thirdparty/git.git
6 weeks agomidx-write.c: enumerate `pack_int_id` values directly
Taylor Blau [Tue, 24 Feb 2026 19:00:26 +0000 (14:00 -0500)] 
midx-write.c: enumerate `pack_int_id` values directly

Our `midx-write.c::fill_packs_from_midx()` function currently enumerates
the range [0, m->num_packs), and then shifts its index variable up by
`m->num_packs_in_base` to produce a valid `pack_int_id`.

Instead, directly enumerate the range:

    [m->num_packs_in_base, m->num_packs_in_base + m->num_packs)

, which are the original pack_int_ids themselves as opposed to the
indexes of those packs relative to the MIDX layer they are contained
within.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx-write.c: extract `fill_pack_from_midx()`
Taylor Blau [Tue, 24 Feb 2026 19:00:21 +0000 (14:00 -0500)] 
midx-write.c: extract `fill_pack_from_midx()`

When filling packs from an existing MIDX, `fill_packs_from_midx()`
handles preparing a MIDX'd pack, and reading out its pack name from the
existing MIDX.

MIDX compaction will want to perform an identical operation, though the
caller will look quite different than `fill_packs_from_midx()`. To
reduce any future code duplication, extract `fill_pack_from_midx()`
from `fill_packs_from_midx()` to prepare to call our new helper function
in a future change.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx-write.c: introduce `midx_pack_perm()` helper
Taylor Blau [Tue, 24 Feb 2026 19:00:17 +0000 (14:00 -0500)] 
midx-write.c: introduce `midx_pack_perm()` helper

The `ctx->pack_perm` array can be considered as a permutation between
the original `pack_int_id` of some given pack to its position in the
`ctx->info` array containing all packs.

Today we can always index into this array with any known `pack_int_id`,
since there is never a `pack_int_id` which is greater than or equal to
the value `ctx->nr`.

That is not necessarily the case with MIDX compaction. For example,
suppose we have a MIDX chain with three layers, each containing three
packs. The base of the MIDX chain will have packs with IDs 0, 1, and 2,
the next layer 3, 4, and 5, and so on. If we are compacting the topmost
two layers, we'll have input `pack_int_id` values between [3, 8], but
`ctx->nr` will only be 6.

In that example, if we want to know where the pack whose original
`pack_int_id` value was, say, 7, we would compute `ctx->pack_perm[7]`,
leading to an uninitialized read, since there are only 6 entries
allocated in that array.

To address this, there are a couple of options:

 - We could allocate enough entries in `ctx->pack_perm` to accommodate
   the largest `orig_pack_int_id` value.

 - Or, we could internally shift the input values by the number of packs
   in the base layer of the lower end of the MIDX compaction range.

This patch prepare us to take the latter approach, since it does not
allocate more memory than strictly necessary. (In our above example, the
base of the lower end of the compaction range is the first MIDX layer
(having three packs), so we would end up indexing `ctx->pack_perm[7-3]`,
which is a valid read.)

Note that this patch does not actually implement that approach yet, but
merely performs a behavior-preserving refactoring which will make the
change easier to carry out in the future.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx: do not require packs to be sorted in lexicographic order
Taylor Blau [Tue, 24 Feb 2026 19:00:13 +0000 (14:00 -0500)] 
midx: do not require packs to be sorted in lexicographic order

The MIDX file format currently requires that pack files be identified by
the lexicographic ordering of their names (that is, a pack having a
checksum beginning with "abc" would have a numeric pack_int_id which is
smaller than the same value for a pack beginning with "bcd").

As a result, it is impossible to combine adjacent MIDX layers together
without permuting bits from bitmaps that are in more recent layer(s).

To see why, consider the following example:

          | packs       | preferred pack
  --------+-------------+---------------
  MIDX #0 | { X, Y, Z } | Y
  MIDX #1 | { A, B, C } | B
  MIDX #2 | { D, E, F } | D

, where MIDX #2's base MIDX is MIDX #1, and so on. Suppose that we want
to combine MIDX layers #0 and #1, to create a new layer #0' containing
the packs from both layers. With the original three MIDX layers, objects
are laid out in the bitmap in the order they appear in their source
pack, and the packs themselves are arranged according to the pseudo-pack
order. In this case, that ordering is Y, X, Z, B, A, C.

But recall that the pseudo-pack ordering is defined by the order that
packs appear in the MIDX, with the exception of the preferred pack,
which sorts ahead of all other packs regardless of its position within
the MIDX. In the above example, that means that pack 'Y' could be placed
anywhere (so long as it is designated as preferred), however, all other
packs must be placed in the location listed above.

Because that ordering isn't sorted lexicographically, it is impossible
to compact MIDX layers in the above configuration without permuting the
object-to-bit-position mapping. Changing this mapping would affect all
bitmaps belonging to newer layers, rendering the bitmaps associated with
MIDX #2 unreadable.

One of the goals of MIDX compaction is that we are able to shrink the
length of the MIDX chain *without* invalidating bitmaps that belong to
newer layers, and the lexicographic ordering constraint is at odds with
this goal.

However, packs do not *need* to be lexicographically ordered within the
MIDX. As far as I can gather, the only reason they are sorted lexically
is to make it possible to perform a binary search over the pack names in
a MIDX, necessary to make `midx_contains_pack()`'s performance
logarithmic in the number of packs rather than linear.

Relax this constraint by allowing MIDX writes to proceed with packs that
are not arranged in lexicographic order. `midx_contains_pack()` will
lazily instantiate a `pack_names_sorted` array on the MIDX, which will
be used to implement the binary search over pack names.

This change produces MIDXs which may not be correctly read with external
tools or older versions of Git. Though older versions of Git know how to
gracefully degrade and ignore any MIDX(s) they consider corrupt,
external tools may not be as robust. To avoid unintentionally breaking
any such tools, guard this change behind a version bump in the MIDX's
on-disk format.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx-write.c: introduce `struct write_midx_opts`
Taylor Blau [Tue, 24 Feb 2026 19:00:09 +0000 (14:00 -0500)] 
midx-write.c: introduce `struct write_midx_opts`

In the MIDX writing code, there are four functions which perform some
sort of MIDX write operation. They are:

 - write_midx_file()
 - write_midx_file_only()
 - expire_midx_packs()
 - midx_repack()

All of these functions are thin wrappers over `write_midx_internal()`,
which implements the bulk of these routines. As a result, the
`write_midx_internal()` function takes six arguments.

Future commits in this series will want to add additional arguments, and
in general this function's signature will be the union of parameters
among *all* possible ways to write a MIDX.

Instead of adding yet more arguments to this function to support MIDX
compaction, introduce a `struct write_midx_opts`, which has the same
struct members as `write_midx_internal()`'s arguments.

Adding additional fields to the `write_midx_opts` struct is preferable
to adding additional arguments to `write_midx_internal()`. This is
because the callers below all zero-initialize the struct, so each time
we add a new piece of information, we do not have to pass the zero value
for it in all other call-sites that do not care about it.

For now, no functional changes are included in this patch.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx-write.c: don't use `pack_perm` when assigning `bitmap_pos`
Taylor Blau [Tue, 24 Feb 2026 19:00:05 +0000 (14:00 -0500)] 
midx-write.c: don't use `pack_perm` when assigning `bitmap_pos`

In midx_pack_order(), we compute for each bitmapped pack the first bit
to correspond to an object in that pack, along with how many bits were
assigned to object(s) in that pack.

Initially, each bitmap_nr value is set to zero, and each bitmap_pos
value is set to the sentinel BITMAP_POS_UNKNOWN. This is done to ensure
that there are no packs who have an unknown bit position but a somehow
non-zero number of objects (cf. `write_midx_bitmapped_packs()` in
midx-write.c).

Once the pack order is fully determined, midx_pack_order() sets the
bitmap_pos field for any bitmapped packs to zero if they are still
listed as BITMAP_POS_UNKNOWN.

However, we enumerate the bitmapped packs in order of `ctx->pack_perm`.
This is fine for existing cases, since the only time the
`ctx->pack_perm` array holds a value outside of the addressable range of
`ctx->info` is when there are expired packs, which only occurs via 'git
multi-pack-index expire', which does not support writing MIDX bitmaps.
As a result, the range of ctx->pack_perm covers all values in [0,
`ctx->nr`), so enumerating in this order isn't an issue.

A future change necessary for compaction will complicate this further by
introducing a wrapper around the `ctx->pack_perm` array, which turns the
given `pack_int_id` into one that is relative to the lower end of the
compaction range. As a result, indexing into `ctx->pack_perm` through
this helper, say, with "0" will produce a crash when the lower end of
the compaction range has >0 pack(s) in its base layer, since the
subtraction will wrap around the 32-bit unsigned range, resulting in an
uninitialized read.

But the process is completely unnecessary in the first place: we are
enumerating all values of `ctx->info`, and there is no reason to process
them in a different order than they appear in memory. Index `ctx->info`
directly to reflect that.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot/t5319-multi-pack-index.sh: fix copy-and-paste error in t5319.39
Taylor Blau [Tue, 24 Feb 2026 19:00:00 +0000 (14:00 -0500)] 
t/t5319-multi-pack-index.sh: fix copy-and-paste error in t5319.39

Commit d4bf1d88b90 (multi-pack-index: verify missing pack, 2018-09-13)
adds a new test to the MIDX test script to test how we handle missing
packs.

While the commit itself describes the test as "verify missing pack[s]",
the test itself is actually called "verify packnames out of order",
despite that not being what it tests.

Likely this was a copy-and-paste of the test immediately above it of the
same name. Correct this by renaming the test to match the commit
message.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agogit-multi-pack-index(1): align SYNOPSIS with 'git multi-pack-index -h'
Taylor Blau [Tue, 24 Feb 2026 18:59:56 +0000 (13:59 -0500)] 
git-multi-pack-index(1): align SYNOPSIS with 'git multi-pack-index -h'

Since c39fffc1c90 (tests: start asserting that *.txt SYNOPSIS matches -h
output, 2022-10-13), the manual page for 'git multi-pack-index' has a
SYNOPSIS section which differs from 'git multi-pack-index -h'.

Correct this while also documenting additional options accepted by the
'write' sub-command.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agogit-multi-pack-index(1): remove non-existent incompatibility
Taylor Blau [Tue, 24 Feb 2026 18:59:52 +0000 (13:59 -0500)] 
git-multi-pack-index(1): remove non-existent incompatibility

Since fcb2205b774 (midx: implement support for writing incremental MIDX
chains, 2024-08-06), the command-line options '--incremental' and
'--bitmap' were declared to be incompatible with one another when
running 'git multi-pack-index write'.

However, since 27afc272c49 (midx: implement writing incremental MIDX
bitmaps, 2025-03-20), that incompatibility no longer exists, despite the
documentation saying so. Correct this by removing the stale reference to
their incompatibility.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agobuiltin/multi-pack-index.c: make '--progress' a common option
Taylor Blau [Tue, 24 Feb 2026 18:59:48 +0000 (13:59 -0500)] 
builtin/multi-pack-index.c: make '--progress' a common option

All multi-pack-index sub-commands (write, verify, repack, and expire)
support a '--progress' command-line option, despite not listing it as
one of the common options in `common_opts`.

As a result each sub-command declares its own `OPT_BIT()` for a
"--progress" command-line option. Centralize this within the
`common_opts` to avoid re-declaring it in each sub-command.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx: introduce `midx_get_checksum_hex()`
Taylor Blau [Tue, 24 Feb 2026 18:59:44 +0000 (13:59 -0500)] 
midx: introduce `midx_get_checksum_hex()`

When trying to print out, say, the hexadecimal representation of a
MIDX's hash, our code will do something like:

    hash_to_hex_algop(midx_get_checksum_hash(m),
                      m->source->odb->repo->hash_algo);

, which is both cumbersome and repetitive. In fact, all but a handful of
callers to `midx_get_checksum_hash()` do exactly the above. Reduce the
repetitive nature of calling `midx_get_checksum_hash()` by having it
return a pointer into a static buffer containing the above result.

For the handful of callers that do need to compare the raw bytes and
don't want to deal with an encoded copy (e.g., because they are passing
it to hasheq() or similar), they may still rely on
`midx_get_checksum_hash()` which returns the raw bytes.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx: rename `get_midx_checksum()` to `midx_get_checksum_hash()`
Taylor Blau [Tue, 24 Feb 2026 18:59:39 +0000 (13:59 -0500)] 
midx: rename `get_midx_checksum()` to `midx_get_checksum_hash()`

Since 541204aabea (Documentation: document naming schema for structs and
their functions, 2024-07-30), we have adopted a naming convention for
functions that would prefer a name like, say, `midx_get_checksum()` over
`get_midx_checksum()`.

Adopt this convention throughout the midx.h API. Since this function
returns a raw (that is, non-hex encoded) hash, let's suffix the function
with "_hash()" to make this clear. As a side effect, this prepares us
for the subsequent change which will introduce a "_hex()" variant that
encodes the checksum itself.

Suggested-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomidx: mark `get_midx_checksum()` arguments as const
Taylor Blau [Tue, 24 Feb 2026 18:59:34 +0000 (13:59 -0500)] 
midx: mark `get_midx_checksum()` arguments as const

To make clear that the function `get_midx_checksum()` does not do
anything to modify its argument, mark the MIDX pointer as const.

The following commit will rename this function altogether to make clear
that it returns the raw bytes of the checksum, not a hex-encoded copy of
it.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agobuild: regenerate config-list.h when Documentation changes
D. Ben Knoble [Tue, 24 Feb 2026 14:39:44 +0000 (09:39 -0500)] 
build: regenerate config-list.h when Documentation changes

The Meson-based build doesn't know when to rebuild config-list.h, so the
header is sometimes stale.

For example, an old build directory might have config-list.h from before
4173df5187 (submodule: introduce extensions.submodulePathConfig,
2026-01-12), which added submodule.<name>.gitdir to the list. Without
it, t9902-completion.sh fails. Regenerating the config-list.h artifact
from sources fixes the artifact and the test.

Since Meson does not have (or want) builtin support for globbing like
Make, teach generate-configlist.sh to also generate a list of
Documentation files its output depends on, and incorporate that into the
Meson build. We honor the undocumented GCC/Clang contract of outputting
empty targets for all the dependencies (like they do with -MP). That is,
generate lines like

    build/config-list.h: $SOURCE_DIR/Documentation/config.adoc
    $SOURCE_DIR/Documentation/config.adoc:

We assume that if a user adds a new file under
Documentation/config then they will also edit one of the existing files
to include that new file, and that will trigger a rebuild. Also mark the
generator script as a dependency.

While we're at it, teach the Makefile to use the same "the script knows
it's dependencies" logic.

For Meson, combining the following commands helps debug dependencies:

    ninja -C <builddir> -t deps config-list.h
    ninja -C <builddir> -t browse config-list.h

The former lists all the dependencies discovered from our output ".d"
file (the config documentation) and the latter shows the dependency on
the script itself, among other useful edges in the dependency graph.

Helped-by: Patrick Steinhardt <ps@pks.im>
Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoMerge branch 'jh/alias-i18n' into jh/alias-i18n-fixes
Junio C Hamano [Tue, 24 Feb 2026 18:50:38 +0000 (10:50 -0800)] 
Merge branch 'jh/alias-i18n' into jh/alias-i18n-fixes

* jh/alias-i18n:
  completion: fix zsh alias listing for subsection aliases
  alias: support non-alphanumeric names via subsection syntax
  alias: prepare for subsection aliases
  help: use list_aliases() for alias listing

6 weeks agobuiltin/maintenance: use "geometric" strategy by default
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:52 +0000 (09:45 +0100)] 
builtin/maintenance: use "geometric" strategy by default

The git-gc(1) command has been introduced in the early days of Git in
30f610b7b0 (Create 'git gc' to perform common maintenance operations.,
2006-12-27) as the main repository maintenance utility. And while the
tool has of course evolved since then to cover new parts, the basic
strategy it uses has never really changed much.

It is safe to say that since 2006 the Git ecosystem has changed quite a
bit. Repositories tend to be much larger nowadays than they have been
almost 20 years ago, and large parts of the industry went crazy for
monorepos (for various wildly different definitions of "monorepo"). So
the maintenance strategy we used back then may not be the best fit
nowadays anymore.

Arguably, most of the maintenance tasks that git-gc(1) does are still
perfectly fine today: repacking references, expiring various data
structures and things like tend to not cause huge problems. But the big
exception is the way we repack objects.

git-gc(1) by default uses a split strategy: it performs incremental
repacks by default, and then whenever we have too many packs we perform
a large all-into-one repack. This all-into-one repack is what is causing
problems nowadays, as it is an operation that is quite expensive. While
it is wasteful in small- and medium-sized repositories, in large repos
it may even be prohibitively expensive.

We have eventually introduced git-maintenance(1) that was slated as a
replacement for git-gc(1). In contrast to git-gc(1), it is much more
flexible as it is structured around configurable tasks and strategies.
So while its default "gc" strategy still uses git-gc(1) under the hood,
it allows us to iterate.

A second strategy it knows about is the "incremental" strategy, which we
configure when registering a repository for scheduled maintenance. This
strategy isn't really a full replacement for git-gc(1) though, as it
doesn't know to expire unused data structures. In Git 2.52 we have thus
introduced a new "geometric" strategy that is a proper replacement for
the old git-gc(1).

In contrast to the incremental/all-into-one split used by git-gc(1), the
new "geometric" strategy maintains a geometric progression of packfiles,
which significantly reduces the number of all-into-one repacks that we
have to perform in large repositories. It is thus a much better fit for
large repositories than git-gc(1).

Note that the "geometric" strategy isn't perfect though: while we
perform way less all-into-one repacks compared to git-gc(1), we still
have to perform them eventually. But for the largest repositories out
there this may not be an option either, as client machines might not be
powerful enough to perform such a repack in the first place. These cases
would thus still be covered by the "incremental" strategy.

Switch the default strategy away from "gc" to "geometric", but retain
the "incremental" strategy configured when registering background
maintenance with `git maintenance register`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot7900: prepare for switch of the default strategy
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:51 +0000 (09:45 +0100)] 
t7900: prepare for switch of the default strategy

The t7900 test suite is exercising git-maintenance(1) and is thus of
course heavily reliant on the exact maintenance strategy. This reliance
comes in two flavors:

  - One test explicitly wants to verify that git-gc(1) is run as part of
    `git maintenance run`. This test is adapted by explicitly picking the
    "gc" strategy.

  - The other tests assume a specific shape of the object database,
    which is dependent on whether or not we run auto-maintenance before
    we come to the actual subject under test. These tests are adapted by
    disabling auto-maintenance.

With these changes t7900 passes with both "gc" and "geometric" default
strategies.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot6500: explicitly use "gc" strategy
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:50 +0000 (09:45 +0100)] 
t6500: explicitly use "gc" strategy

The test in t6500 explicitly wants to exercise git-gc(1) and is thus
highly specific to the actual on-disk state of the repository and
specifically of the object database. An upcoming change modifies the
default maintenance strategy to be the "geometric" strategy though,
which breaks a couple of assumptions.

One fix would arguably be to disable auto-maintenance altogether, as we
do want to explicitly verify git-gc(1) anyway. But as the whole test
suite is about git-gc(1) in the first place it feels more sensible to
configure the default maintenance strategy to be "gc".

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot5510: explicitly use "gc" strategy
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:49 +0000 (09:45 +0100)] 
t5510: explicitly use "gc" strategy

One of the tests in t5510 wants to verify that auto-gc does not lock up
when fetching into a repository. Adapt it to explicitly pick the "gc"
strategy for auto-maintenance.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot5400: explicitly use "gc" strategy
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:48 +0000 (09:45 +0100)] 
t5400: explicitly use "gc" strategy

In t5400 we verify that git-receive-pack(1) runs automated repository
maintenance in the remote repository. The check is performed indirectly
by observing an effect that git-gc(1) would have, namely to prune a
temporary object from the object database. In a subsequent commit we're
about to switch to the "geometric" strategy by default though, and here
we stop observing that effect.

Adapt the test to explicitly use the "gc" strategy to prepare for that
upcoming change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot34xx: don't expire reflogs where it matters
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:47 +0000 (09:45 +0100)] 
t34xx: don't expire reflogs where it matters

We have a couple of tests in the t34xx range that rely on reflogs. This
never really used to be a problem, but in a subsequent commit we will
change the default maintenance strategy from "gc" to "geometric", and
this will cause us to drop all reflogs in these tests.

This may seem surprising and like a bug at first, but it's actually not.
The main difference between these two strategies is that the "gc"
strategy will skip all maintenance in case the object database is in a
well-optimized state. The "geometric" strategy has separate subtasks
though, and the conditions for each of these tasks is evaluated on a
case by case basis. This means that even if the object database is in
good shape, we may still decide to expire reflogs.

So why is that a problem? The issue is that Git's test suite hardcodes
the committer and author dates to a date in 2005. Interestingly though,
these hardcoded dates not only impact the commits, but also the reflog
entries. The consequence is that all newly written reflog entries are
immediately considered stale as our reflog expiration threshold is in
the range of weeks, only. It follows that executing `git reflog expire`
will thus immediately purge all reflog entries.

This hasn't been a problem in our test suite by pure chance, as the
repository shapes simply didn't cause us to perform actual garbage
collection. But with the upcoming "geometric" strategy we _will_ start
to execute `git reflog expire`, thus surfacing this issue.

Prepare for this by explicitly disabling reflog expiration in tests
impacted by this upcoming change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot: disable maintenance where we verify object database structure
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:46 +0000 (09:45 +0100)] 
t: disable maintenance where we verify object database structure

We have a couple of tests that explicitly verify the structure of the
object database. Naturally, this structure is dependent on whether or
not we run repository maintenance: if it decides to optimize the object
database the expected structure is likely to not materialize.

Explicitly disable auto-maintenance in such tests so that we are not
dependent on decisions made by our maintenance.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot: fix races caused by background maintenance
Patrick Steinhardt [Tue, 24 Feb 2026 08:45:45 +0000 (09:45 +0100)] 
t: fix races caused by background maintenance

Many Git commands spawn git-maintenance(1) to optimize the repository in
the background. By default, performing the maintenance is for most of
the part asynchronous: we fork the executable and then continue with the
rest of our business logic.

This is working as expected for our users, but this behaviour is
somewhat problematic for our test suite as this is inherently racy. We
have many tests that verify the on-disk state of repositories, and those
tests may easily race with our background maintenance. In a similar
fashion, we may end up with processes that "leak" out of a current test
case.

Until now this tends to not be much of a problem. Our maintenance uses
git-gc(1) by default, which knows to bail out in case there aren't
either too many packfiles or too many loose objects. So even if other
data structures would need to be optimized, we won't do so unless the
object database also needs optimizations.

This is about to change though, as a subsequent commit will switch to
the "geometric" maintenance strategy as a default. The consequence is
that we will run required optimizations even if the object database is
well-optimized. And this uncovers races between our test suite and
background maintenance all over the place.

Disabling maintenance outright in our test suite is not really an
option, as it would result in significant divergence from the "real
world" and reduce our test coverage. But we've got an alternative up our
sleeves: we can ensure that garbage collection runs synchronously by
overriding the "maintenance.autoDetach" configuration.

Of course that also diverges from the real world, as we now stop testing
that background maintenance interacts in a benign way with normal Git
commands. But on the other hand this ensures that the maintenance itself
does not for example lead to data loss in a more reproducible way.

Another concern is that this would make execution of the test suite much
slower. But a quick benchmark on my machine demonstrates that this does
not seem to be the case:

    Benchmark 1: meson test (revision = HEAD~)
      Time (mean ± σ):     131.182 s ±  1.293 s    [User: 853.737 s, System: 1160.479 s]
      Range (min … max):   130.001 s … 132.563 s    3 runs

    Benchmark 2: meson test (revision = HEAD)
      Time (mean ± σ):     129.554 s ±  0.507 s    [User: 849.040 s, System: 1152.664 s]
      Range (min … max):   129.000 s … 129.994 s    3 runs

    Summary
      meson test (revision = HEAD) ran
        1.01 ± 0.01 times faster than meson test (revision = HEAD~)

Funny enough, it even seems as if this speeds up test execution ever so
slightly, but that may just as well be noise.

Introduce a new `GIT_TEST_MAINT_AUTO_DETACH` environment variable that
allows us to override the auto-detach behaviour and set that variable in
our tests.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agodiffcore-break: avoid segfault with freed entries
Han Young [Tue, 24 Feb 2026 06:13:29 +0000 (14:13 +0800)] 
diffcore-break: avoid segfault with freed entries

After we have freed the file pair, we should set the queue reference to null.
When computing a diff in a partial clone, there is a chance that we
could trigger a prefetch of missing objects when there are freed entries in
the global diff queue due to break-rewrites detection. The segfault only occurs
if an entry has been freed by break-rewrites and there is an entry
to be prefetched.

There is a new test in t4067 that trigger the segmentation fault that results
in this case. The test explicitly fetch the necessary blobs to trigger the
break rewrites, some blobs are left to be prefetched.

The fix is to set the queue pointer to NULL after it is freed, the prefetch
will skip NULL entries.

Signed-off-by: Han Young <hanyang.tony@bytedance.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agodoc: diff-options.adoc: show format.noprefix for format-patch
Kristoffer Haugsbakk [Mon, 23 Feb 2026 23:30:51 +0000 (00:30 +0100)] 
doc: diff-options.adoc: show format.noprefix for format-patch

git-format-patch(1) uses `format.noprefix` and ignores `diff.noprefix`.

The configuration variable `format.prefix` was added as an “escape
hatch”, and “it’s unlikely that anybody really wants format.
noprefix=true in the first place.”[1] Based on that there doesn’t
seem to be a need to widely advertise this configuration variable.

But in any case: the documentation for this option should not claim
that it overrides a config that is always ignored.

† 1: 8d5213de (format-patch: add format.noprefix option, 2023-03-09)

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoformat-patch: make format.noprefix a boolean
Kristoffer Haugsbakk [Mon, 23 Feb 2026 23:30:50 +0000 (00:30 +0100)] 
format-patch: make format.noprefix a boolean

The config `format.noprefix` was added in 8d5213de (format-patch: add
format.noprefix option, 2023-03-09) to support no-prefix on paths.
That was immediately after making git-format-patch(1) not respect
`diff.noprefix`.[1]

The intent was to mirror `diff.noprefix`. But this config was
unintentionally[2] implemented by enabling no-prefix if any kind of
value is set.

† 1: c169af8f (format-patch: do not respect diff.noprefix, 2023-03-09)
† 2: https://lore.kernel.org/all/20260211073553.GA1867915@coredump.intra.peff.net/

Let’s indeed mirror `diff.noprefix` by treating it as a boolean.

This is a breaking change. And as far as breaking changes go it is
pretty benign:

• The documentation claims that this config is equivalent to
  `diff.noprefix`; this is just a bug fix if the documentation is
  what defines the application interface
• Only users with non-boolean values will run into problems when we
  try to parse it as a boolean. But what would (1) make them suspect
  they could do that in the first place, and (2) have motivated them to
  do it?
• Users who have set this to `false` and expect that to mean *enable
  format.noprefix* (current behavior) will now have the opposite
  experience. Which is not a reasonable setup.

Let’s only offer a breaking change fig leaf by advising about the
previous behavior before dying.

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoMerge branch 'ps/object-info-bits-cleanup' into ps/odb-sources
Junio C Hamano [Mon, 23 Feb 2026 21:48:48 +0000 (13:48 -0800)] 
Merge branch 'ps/object-info-bits-cleanup' into ps/odb-sources

* ps/object-info-bits-cleanup:
  odb: convert `odb_has_object()` flags into an enum
  odb: convert object info flags into an enum
  odb: drop gaps in object info flag values
  builtin/fsck: fix flags passed to `odb_has_object()`
  builtin/backfill: fix flags passed to `odb_has_object()`

6 weeks agoMerge branch 'ps/odb-for-each-object' into ps/odb-sources
Junio C Hamano [Mon, 23 Feb 2026 21:48:00 +0000 (13:48 -0800)] 
Merge branch 'ps/odb-for-each-object' into ps/odb-sources

* ps/odb-for-each-object:
  odb: drop unused `for_each_{loose,packed}_object()` functions
  reachable: convert to use `odb_for_each_object()`
  builtin/pack-objects: use `packfile_store_for_each_object()`
  odb: introduce mtime fields for object info requests
  treewide: drop uses of `for_each_{loose,packed}_object()`
  treewide: enumerate promisor objects via `odb_for_each_object()`
  builtin/fsck: refactor to use `odb_for_each_object()`
  odb: introduce `odb_for_each_object()`
  packfile: introduce function to iterate through objects
  packfile: extract function to iterate through objects of a store
  object-file: introduce function to iterate through objects
  object-file: extract function to read object info from path
  odb: fix flags parameter to be unsigned
  odb: rename `FOR_EACH_OBJECT_*` flags

6 weeks agoconfig: use an enum for type
Derrick Stolee [Mon, 23 Feb 2026 12:26:55 +0000 (12:26 +0000)] 
config: use an enum for type

The --type=<X> option for 'git config' has previously been defined using
macros, but using a typed enum is better for tracking the possible
values.

Move the definition up to make sure it is defined before a macro uses
some of its terms.

Update the initializer for config_display_options to explicitly set
'type' to TYPE_NONE even though this is implied by a zero value.

This assists in knowing that the switch statement added in the previous
change has a complete set of cases for a properly-valued enum.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: restructure format_config()
Derrick Stolee [Mon, 23 Feb 2026 12:26:54 +0000 (12:26 +0000)] 
config: restructure format_config()

The recent changes have replaced the bodies of most if/else-if cases
with simple helper method calls. This makes it easy to adapt the
structure into a clearer switch statement, leaving a simple if/else in
the default case.

Make things a little simpler to read by reducing the nesting depth via a
new goto statement when we want to skip values.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: format colors quietly
Derrick Stolee [Mon, 23 Feb 2026 12:26:53 +0000 (12:26 +0000)] 
config: format colors quietly

Move the logic for formatting color config value into a helper method
and use quiet parsing when needed.

This removes error messages when parsing a list of config values that do
not match color formats.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agocolor: add color_parse_quietly()
Derrick Stolee [Mon, 23 Feb 2026 12:26:52 +0000 (12:26 +0000)] 
color: add color_parse_quietly()

When parsing colors, a failed parse leads to an error message due to the
result returning error(). To allow for quiet parsing, create
color_parse_quietly(). This is in contrast to an ..._gently() version
because the original does not die(), so both options are technically
'gentle'.

To accomplish this, convert the implementation of color_parse_mem() into
a static color_parse_mem_1() helper that adds a 'quiet' parameter. The
color_parse_quietly() method can then use this. Since it is a near
equivalent to color_parse(), move that method down in the file so they
can be nearby while also appearing after color_parse_mem_1().

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: format expiry dates quietly
Derrick Stolee [Mon, 23 Feb 2026 12:26:51 +0000 (12:26 +0000)] 
config: format expiry dates quietly

Move the logic for formatting expiry date config values into a helper
method and use quiet parsing when needed.

Note that git_config_expiry_date() will show an error on a bad parse and
not die() like most other git_config...() parsers. Thus, we use
'quietly' here instead of 'gently'.

There is an unfortunate asymmetry in these two parsing methods, but we
need to treat a positive response from parse_expiry_date() as an error
or we will get incorrect values.

This updates the behavior of 'git config list --type=expiry-date' to be
quiet when attempting parsing on non-date values.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: format paths gently
Derrick Stolee [Mon, 23 Feb 2026 12:26:50 +0000 (12:26 +0000)] 
config: format paths gently

Move the logic for formatting path config values into a helper method
and use gentle parsing when needed.

We need to be careful about how to handle the ':(optional)' macro, which
as tested in t1311-config-optional.sh must allow for ignoring a missing
path when other multiple values exist, but cause 'git config get' to
fail if it is the only possible value and thus no result is output.

In the case of our list, we need to omit those values silently. This
necessitates the use of the 'gently' parameter here.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: format bools or strings in helper
Derrick Stolee [Mon, 23 Feb 2026 12:26:49 +0000 (12:26 +0000)] 
config: format bools or strings in helper

Move the logic for formatting bool-or-string config values into a
helper. This parsing has always been gentle, so this is not unlocking
new behavior. This extraction is only to match the formatting of the
other cases that do need a behavior change.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: format bools or ints gently
Derrick Stolee [Mon, 23 Feb 2026 12:26:48 +0000 (12:26 +0000)] 
config: format bools or ints gently

Move the logic for formatting bool-or-int config values into a helper
method and use gentle parsing when needed.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: format bools gently
Derrick Stolee [Mon, 23 Feb 2026 12:26:47 +0000 (12:26 +0000)] 
config: format bools gently

Move the logic for formatting bool config values into a helper method
and use gentle parsing when needed.

This makes 'git config list --type=bool' not fail when coming across a
non-boolean value. Such unparseable values are filtered out quietly.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: format int64s gently
Derrick Stolee [Mon, 23 Feb 2026 12:26:46 +0000 (12:26 +0000)] 
config: format int64s gently

Move the logic for formatting int64 config values into a helper method
and use gentle parsing when needed.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: make 'git config list --type=<X>' work
Derrick Stolee [Mon, 23 Feb 2026 12:26:45 +0000 (12:26 +0000)] 
config: make 'git config list --type=<X>' work

Previously, the --type=<X> argument to 'git config list' was ignored and
did nothing. Now, we add the use of format_config() to the
show_all_config() function so each key-value pair is attempted to be
parsed. This is our first use of the 'gently' parameter with a nonzero
value.

When listing multiple values, our initial settings for the output format
is different. Add a new init helper to specify the fact that keys should
be shown and also add the default delimiters as they were unset in some
cases.

Our intention is that if there is an error in parsing, then the row is
not output. This is necessary to avoid the caller needing to build their
own validator to understand the difference between valid, canonicalized
types and other raw string values. The raw values will always be
available to the user if they do not specify the --type=<X> option.

The current behavior is more complicated, including error messages on
bad parsing or potentially complete failure of the command.  We add
tests at this point that demonstrate the current behavior so we can
witness the fix in future changes that parse these values quietly and
gently.

This is a change in behavior! We are starting to respect an option that
was previously ignored, leading to potential user confusion. This is
probably still a good option, since the --type argument did not change
behavior at all previously, so users can get the behavior they expect by
removing the --type argument or adding the --no-type argument.

t1300-config.sh is updated with the current behavior of this formatting
logic to justify the upcoming refactoring of format_config() that will
incrementally fix some of these cases to be more user-friendly.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: add 'gently' parameter to format_config()
Derrick Stolee [Mon, 23 Feb 2026 12:26:44 +0000 (12:26 +0000)] 
config: add 'gently' parameter to format_config()

This parameter is set to 0 for all current callers and is UNUSED.
However, we will start using this option in future changes and in a
critical change that requires gentle parsing (not using die()) to try
parsing all values in a list.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoconfig: move show_all_config()
Derrick Stolee [Mon, 23 Feb 2026 12:26:43 +0000 (12:26 +0000)] 
config: move show_all_config()

In anticipation of using format_config() in this method, move
show_all_config() lower in the file without changes.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: replace `refs_for_each_fullref_in()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:51 +0000 (12:59 +0100)] 
refs: replace `refs_for_each_fullref_in()`

Replace calls to `refs_for_each_fullref_in()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: replace `refs_for_each_namespaced_ref()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:50 +0000 (12:59 +0100)] 
refs: replace `refs_for_each_namespaced_ref()`

Replace calls to `refs_for_each_namespaced_ref()` with the newly
introduced `refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: replace `refs_for_each_glob_ref()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:49 +0000 (12:59 +0100)] 
refs: replace `refs_for_each_glob_ref()`

Replace calls to `refs_for_each_glob_ref()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: replace `refs_for_each_glob_ref_in()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:48 +0000 (12:59 +0100)] 
refs: replace `refs_for_each_glob_ref_in()`

Replace calls to `refs_for_each_glob_ref_in()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: replace `refs_for_each_rawref_in()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:47 +0000 (12:59 +0100)] 
refs: replace `refs_for_each_rawref_in()`

Replace calls to `refs_for_each_rawref_in()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: replace `refs_for_each_rawref()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:46 +0000 (12:59 +0100)] 
refs: replace `refs_for_each_rawref()`

Replace calls to `refs_for_each_rawref()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: replace `refs_for_each_ref_in()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:45 +0000 (12:59 +0100)] 
refs: replace `refs_for_each_ref_in()`

Replace calls to `refs_for_each_ref_in()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: improve verification for-each-ref options
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:44 +0000 (12:59 +0100)] 
refs: improve verification for-each-ref options

Improve verification of the passed-in for-each-ref options:

  - Require that the `refs` store must be given. It's arguably very
    surprising that we simply return successfully in case the ref store
    is a `NULL` pointer.

  - When expected to trim ref prefixes we will `BUG()` in case the
    refname would become empty or in case we're expected to trim a
    longer prefix than the refname is long. As such, this case is only
    guaranteed to _not_ `BUG()` in case the caller also specified a
    prefix. And furthermore, that prefix must end in a trailing slash,
    as otherwise it may produce an exact match that could lead us to
    trim to the empty string.

An audit shows that there are no callsites that rely on either of these
behaviours, so this should not result in a functional change.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: generalize `refs_for_each_fullref_in_prefixes()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:43 +0000 (12:59 +0100)] 
refs: generalize `refs_for_each_fullref_in_prefixes()`

The function `refs_for_each_fullref_in_prefixes()` can be used to
iterate over all references part of any of the user-provided prefixes.
In contrast to the `prefix` parameter of `refs_for_each_ref_ext()` it
knows to handle the case well where multiple of the passed-in prefixes
start with a common prefix by computing longest common prefixes and then
iterating over those.

While we could move this logic into `refs_for_each_ref_ext()`, this one
feels somewhat special as we perform multiple iterations. But what we
_can_ do is to generalize how this function works: instead of accepting
only a small handful of parameters, we can have it accept the full
options structure.

One obvious exception is that the caller must not provide a prefix via
the options. But this case can be easily detected.

Refactor the code accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: generalize `refs_for_each_namespaced_ref()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:42 +0000 (12:59 +0100)] 
refs: generalize `refs_for_each_namespaced_ref()`

The function `refs_for_each_namespaced_ref()` iterates through all
references that are part of the current ref namespace. This namespace
can be configured by setting the `GIT_NAMESPACE` environment variable
and is then retrieved by calling `get_git_namespace()`.

If a namespace is configured, then we:

  - Obviously only yield refs that exist in this namespace.

  - Rewrite exclude patterns so that they work for the given namespace,
    if any namespace is currently configured.

Port this logic to `refs_for_each_ref_ext()` by adding a new `namespace`
field to the options structure. This gives callers more flexibility as
they can decide by themselves whether they want to use the globally
configured or an arbitrary other namespace.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: speed up `refs_for_each_glob_ref_in()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:41 +0000 (12:59 +0100)] 
refs: speed up `refs_for_each_glob_ref_in()`

The function `refs_for_each_glob_ref_in()` can be used to iterate
through all refs in a specific prefix with globbing. The logic to handle
this is currently hosted by `refs_for_each_glob_ref_in()`, which sets up
a callback function that knows to filter out refs that _don't_ match the
given globbing pattern.

The way we do this is somewhat inefficient though: even though the
function is expected to only yield refs in the given prefix, we still
end up iterating through _all_ references, regardless of whether or not
their name matches the given prefix.

Extend `refs_for_each_ref_ext()` so that it can handle patterns and
adapt `refs_for_each_glob_ref_in()` to use it. This means we continue to
use the same callback-based infrastructure to filter individual refs via
the globbing pattern, but we can now also use the other functionality of
the `_ext()` variant.

Most importantly, this means that we now properly handle the prefix.
This results in a performance improvement when using a prefix where a
significant majority of refs exists outside of the prefix. The following
benchmark is an extreme case, with 1 million refs that exist outside the
prefix and a single ref that exists inside it:

    Benchmark 1: git rev-parse --branches=refs/heads/* (rev = HEAD~)
      Time (mean ± σ):     115.9 ms ±   0.7 ms    [User: 113.0 ms, System: 2.4 ms]
      Range (min … max):   114.9 ms … 117.8 ms    25 runs

    Benchmark 2: git rev-parse --branches=refs/heads/* (rev = HEAD)
      Time (mean ± σ):       1.1 ms ±   0.1 ms    [User: 0.3 ms, System: 0.7 ms]
      Range (min … max):     1.0 ms …   2.3 ms    2092 runs

    Summary
      git rev-parse --branches=refs/heads/* (rev = HEAD) ran
      107.01 ± 6.49 times faster than git rev-parse --branches=refs/heads/* (rev = HEAD~)

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: introduce `refs_for_each_ref_ext`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:40 +0000 (12:59 +0100)] 
refs: introduce `refs_for_each_ref_ext`

In the refs subsystem we have a proliferation of functions that all
iterate through references. (Almost) all of these functions internally
call `do_for_each_ref()` and provide slightly different arguments so
that one can control different aspects of its behaviour. This approach
doesn't really scale: every time there is a slightly different use case
for iterating through refs we create another new function.

This combinatorial explosion doesn't make a lot of sense: it leads to
confusing interfaces and heightens the maintenance burden.

Refactor the code to become more composable by:

  - Exposing `do_for_each_ref()` as `refs_for_each_ref_ext()`.

  - Introducing an options structure that lets the caller control
    individual options.

This gives us a much better foundation to build on going forward.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: rename `each_ref_fn`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:39 +0000 (12:59 +0100)] 
refs: rename `each_ref_fn`

Similar to the preceding commit, rename `each_ref_fn` to better match
our current best practices around how we name things.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: rename `do_for_each_ref_flags`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:38 +0000 (12:59 +0100)] 
refs: rename `do_for_each_ref_flags`

The enum `do_for_each_ref_flags` and its individual values don't match
to our current best practices when it comes to naming things. Rename it
to `refs_for_each_flag`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: move `do_for_each_ref_flags` further up
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:37 +0000 (12:59 +0100)] 
refs: move `do_for_each_ref_flags` further up

Move the `do_for_each_ref_flags` enum further up. This prepares for
subsequent changes, where the flags will be used by more functions.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: move `refs_head_ref_namespaced()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:36 +0000 (12:59 +0100)] 
refs: move `refs_head_ref_namespaced()`

The function `refs_head_ref_namespaced()` is somewhat special when
compared to most of the other functions that take a callback function:
while `refs_for_each_*()` functions yield multiple refs,
`refs_heasd_ref_namespaced()` will only yield at most the HEAD ref of
the current namespace. As such, the function is related to
`refs_head_ref()` and not to the for-each functions.

Move the function to be located next to `refs_head_ref()` to clarify.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agorefs: remove unused `refs_for_each_include_root_ref()`
Patrick Steinhardt [Mon, 23 Feb 2026 11:59:35 +0000 (12:59 +0100)] 
refs: remove unused `refs_for_each_include_root_ref()`

Remove the unused `refs_for_each_include_root_ref()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agopack-check: fix verification of large objects
Patrick Steinhardt [Mon, 23 Feb 2026 16:00:09 +0000 (17:00 +0100)] 
pack-check: fix verification of large objects

It was reported [1] that git-fsck(1) may sometimes run into an infinite
loop when processing packfiles. This bug was bisected to c31bad4f7d
(packfile: track packs via the MRU list exclusively, 2025-10-30), which
refactored our lsit of packfiles to only be tracked via an MRU list,
exclusively. This isn't entirely surprising: any caller that iterates
through the list of packfiles and then hits `find_pack_entry()`, for
example because they read an object from it, may cause the MRU list to
be updated. And if the caller is unlucky, this may cause the mentioned
infinite loop.

While this mechanism is somewhat fragile, it is still surprising that we
encounter it when verifying the packfile. We iterate through objects in
a given pack one by one and then read them via their offset, and doing
this shouldn't ever end up in `find_pack_entry()`.

But there is an edge case here: when the object in question is a blob
bigger than "core.largeFileThreshold", then we will be careful to not
read it into memory. Instead, we read it via an object stream by calling
`odb_read_object_stream()`, and that function will perform an object
lookup via `odb_read_object_info()`. So in the case where there are at
least two blobs in two different packfiles, and both of these blobs
exceed "core.largeFileThreshold", then we'll run into an infinite loop
because we'll always update the MRU.

We could fix this by improving `repo_for_each_pack()` to not update the
MRU, and this would address the issue. But the fun part is that using
`odb_read_object_stream()` is the wrong thing to do in the first place:
it may open _any_ instance of this object, so we ultimately cannot be
sure that we even verified the object in our given packfile.

Fix this bug by creating the object stream for the packed object
directly via `packfile_read_object_stream()`. Add a test that would have
caused the infinite loop.

[1]: <20260222183710.2963424-1-sandals@crustytoothpaste.net>

Reported-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agopackfile: expose function to read object stream for an offset
Patrick Steinhardt [Mon, 23 Feb 2026 16:00:08 +0000 (17:00 +0100)] 
packfile: expose function to read object stream for an offset

The function `packfile_store_read_object_stream()` takes as input an
object ID and then constructs a `struct odb_read_stream` from it. In a
subsequent commit we'll want to create an object stream for a given
combination of packfile and offset though, which is not something that
can currently be done.

Extract a new function `packfile_read_object_stream()` that makes this
functionality available.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoobject-file: adapt `stream_object_signature()` to take a stream
Patrick Steinhardt [Mon, 23 Feb 2026 16:00:07 +0000 (17:00 +0100)] 
object-file: adapt `stream_object_signature()` to take a stream

The function `stream_object_signature()` is responsible for verifying
whether the given object ID matches the actual hash of the object's
contents. In contrast to `check_object_signature()` it does so in a
streaming fashion so that we don't have to load the full object into
memory.

In a subsequent commit we'll want to adapt one of its callsites to pass
a preconstructed stream. Prepare for this by accepting a stream as input
that the caller needs to assemble.

While at it, improve the error reporting in `parse_object_with_flags()`
to tell apart the two failure modes.

Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot/helper: improve "genrandom" test helper
Patrick Steinhardt [Mon, 23 Feb 2026 16:00:06 +0000 (17:00 +0100)] 
t/helper: improve "genrandom" test helper

The `test-tool genrandom` test helper can be used to generate random
data, either as an infinite stream or with a specified number of bytes.
The way we handle parsing the number of bytes is lacking though:

  - We don't have good error handling, so if the caller for example uses
    `test-tool genrandom 200xyz` then we'll end up generating 200 bytes
    of random data successfully.

  - Many callers want to generate e.g. 1 kilobyte or megabyte of data,
    but they have to either use unwieldy numbers like 1048576, or they
    have to precompute them.

Fix both of these issues by using `git_parse_ulong()` to parse the
argument. This function has better error handling, and it knows to
handle unit suffixes.

Adapt a couple of our tests to use suffixes instead of manual
computations.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoobject-file.c: avoid container_of() of a NULL container
Junio C Hamano [Sun, 22 Feb 2026 20:16:19 +0000 (12:16 -0800)] 
object-file.c: avoid container_of() of a NULL container

Even though the "struct odb_transaction" member is at the beginning
of the containing "struct odb_transaction_files", i.e., at offset 0,
using container_of() to add offset 0 to a NULL pointer gets flagged
as a bad behaviour under SANITIZE=undefined.

Use container_of_or_null() to work around this issue.

Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agopack-redundant: fix memory leak when open_pack_index() fails
Sahitya Chandra [Sat, 21 Feb 2026 10:38:59 +0000 (16:08 +0530)] 
pack-redundant: fix memory leak when open_pack_index() fails

In add_pack(), we allocate l.remaining_objects with llist_init() before
calling open_pack_index(). If open_pack_index() fails we return NULL
without freeing the allocated list, leaking the memory.

Fix by calling llist_free(l.remaining_objects) on the error path before
returning.

Signed-off-by: Sahitya Chandra <sahityajb@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agopath: factor out skip_slashes() in normalize_path_copy_len()
Pushkar Singh [Sat, 21 Feb 2026 11:05:12 +0000 (11:05 +0000)] 
path: factor out skip_slashes() in normalize_path_copy_len()

Extract skip_slashes() to avoid repeating the same is_dir_sep()
loop in multiple places inside normalize_path_copy_len().

Keep the dot-component handling inline to preserve the original
control flow and readability, as suggested in review.

No functional changes. Behavior verified with t0060-path-utils.sh.

Signed-off-by: Pushkar Singh <pushkarkumarsingh1970@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agot2004: use test_path_is_file instead of test -f
Lambert Duclos-de Guise [Sat, 21 Feb 2026 17:28:13 +0000 (17:28 +0000)] 
t2004: use test_path_is_file instead of test -f

Replace 'test -f' with the helper function 'test_path_is_file'
to provide better error messages upon failure.

Signed-off-by: Lambert Duclos-de Guise <lambertddg@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agoreplay: prevent the_repository from coming back
Elijah Newren [Fri, 20 Feb 2026 01:59:48 +0000 (01:59 +0000)] 
replay: prevent the_repository from coming back

Due to the use of DEFAULT_ABBREV, we cannot get rid of our usage of
USE_THE_REPOSITORY_VARIABLE.  We have removed all other uses of
the_repository before, but without removing that definition, they keep
coming back.

Define the_repository to make it a compilation error so that they don't
come back any more; the repo parameter plumbed through the various
functions can be used instead.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomerge-ort: prevent the_repository from coming back
Elijah Newren [Sat, 21 Feb 2026 23:59:52 +0000 (23:59 +0000)] 
merge-ort: prevent the_repository from coming back

Due to the use of DEFAULT_ABBREV, we cannot get rid of our usage of
USE_THE_REPOSITORY_VARIABLE.  However, we have removed all other uses of
the_repository in merge-ort a few times.  But they keep coming back.

Define the_repository to make it a compilation error so that they don't
come back any more.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomerge-ort: replace the_hash_algo with opt->repo->hash_algo
Elijah Newren [Sat, 21 Feb 2026 23:59:51 +0000 (23:59 +0000)] 
merge-ort: replace the_hash_algo with opt->repo->hash_algo

We have a perfectly valid repository available and do not need to use
the_hash_algo (a shorthand for the_repository->hash_algo), so use the
known repository instead.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomerge-ort: replace the_repository with opt->repo
Elijah Newren [Sat, 21 Feb 2026 23:59:50 +0000 (23:59 +0000)] 
merge-ort: replace the_repository with opt->repo

We have a perfectly valid repository available and do not need to use
the_repository.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomerge-ort: pass repository to write_tree()
Elijah Newren [Sat, 21 Feb 2026 23:59:49 +0000 (23:59 +0000)] 
merge-ort: pass repository to write_tree()

In order to get rid of a usage of the_repository, we need to know the
value of opt->repo; pass it along to write_tree().  Once we have the
repository, though, we no longer need to pass
opt->repo->hash_algo->rawsz, we can have write_tree() look up that value
itself.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
6 weeks agomerge,diff: remove the_repository check before prefetching blobs
Elijah Newren [Sat, 21 Feb 2026 23:59:48 +0000 (23:59 +0000)] 
merge,diff: remove the_repository check before prefetching blobs

Prefetching of blobs from promisor remotes was added to diff in
7fbbcb21b162 (diff: batch fetching of missing blobs, 2019-04-05).  In
that commit,

  https://lore.kernel.org/git/20190405170934.20441-1-jonathantanmy@google.com/

was squashed into

  https://lore.kernel.org/git/44de02e584f449481e6fb00cf35d74adf0192e9d.1553895166.git.jonathantanmy@google.com/

without the extra explanation about the squashed changes being added to
the commit message; in particular, this explanation from that first link
is absent:

> Also, prefetch only if the repository being diffed is the_repository
> (because we do not support lazy fetching for any other repository
>  anyway).

Then, later, this checking was spread from diff.c to diffcore-rename.c
and diffcore-break.c by 95acf11a3dc3 (diff: restrict when prefetching
occurs, 2020-04-07) and then further split in d331dd3b0c82
(diffcore-rename: allow different missing_object_cb functions,
2021-06-22).  I also copied the logic from prefetching blobs from
diff.c to merge-ort.c in 2bff554b23e8 (merge-ort: add prefetching for
content merges, 2021-06-22).

The reason for all these checks was noted above -- we only supported
lazy fetching for the_repository.  However, that changed with
ef830cc43412 (promisor-remote: teach lazy-fetch in any repo,
2021-06-17), so these checks are now unnecessary.  Remove them.

Signed-off-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agosymlinks: use unsigned int for flags
Tian Yuchen [Mon, 16 Feb 2026 17:20:28 +0000 (01:20 +0800)] 
symlinks: use unsigned int for flags

The 'flags' and 'track_flags' fields in symlinks.c are used
strictly as a collection of bits (using bitwise operators including
&, |, ~). Using a signed integer for bitmasks may lead to undefined
behavior with shift operations and logic errors if the MSB is touched.

Change these fields from 'int' to 'unsigned int' to match our usage
patterns.

Signed-off-by: Tian Yuchen <a3205153416@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agoThe 6th batch
Junio C Hamano [Fri, 20 Feb 2026 18:28:05 +0000 (10:28 -0800)] 
The 6th batch

Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agoMerge branch 'ak/t9812-test-path-is-helpers'
Junio C Hamano [Fri, 20 Feb 2026 19:36:18 +0000 (11:36 -0800)] 
Merge branch 'ak/t9812-test-path-is-helpers'

Test update.

* ak/t9812-test-path-is-helpers:
  t9812: modernize test path helpers

7 weeks agoMerge branch 'pw/diff-anchored-optim'
Junio C Hamano [Fri, 20 Feb 2026 19:36:18 +0000 (11:36 -0800)] 
Merge branch 'pw/diff-anchored-optim'

"git diff --anchored=<text>" has been optimized.

* pw/diff-anchored-optim:
  diff --anchored: avoid checking unmatched lines

7 weeks agoMerge branch 'jc/doc-cg-c-comment'
Junio C Hamano [Fri, 20 Feb 2026 19:36:18 +0000 (11:36 -0800)] 
Merge branch 'jc/doc-cg-c-comment'

A CodingGuidelines update.

* jc/doc-cg-c-comment:
  CodingGuidelines: document // comments

7 weeks agoMerge branch 'pw/xdiff-cleanups'
Junio C Hamano [Fri, 20 Feb 2026 19:36:17 +0000 (11:36 -0800)] 
Merge branch 'pw/xdiff-cleanups'

Small clean-up of xdiff library to remove unnecessary data
duplication.

* pw/xdiff-cleanups:
  xdiff: remove unused data from xdlclass_t
  xdiff: remove "line_hash" field from xrecord_t

7 weeks agotree-diff: remove the usage of the_hash_algo global
Shreyansh Paliwal [Fri, 20 Feb 2026 17:51:26 +0000 (23:21 +0530)] 
tree-diff: remove the usage of the_hash_algo global

emit_path() uses the global the_hash_algo even though a local repository is
already available via struct diff_options *opt.

Replace these uses with opt->repo->hash_algo. With no remaining reliance on
global states in this file, drop the dependency on 'environment.h' and remove
'#define USE_THE_REPOSITORY_VARIABLE'.

This follows earlier cleanups to introduce opt->repo in tree-diff.c [1][2].

[1]- https://lore.kernel.org/git/20180921155739.14407-21-pclouds@gmail.com/
[2]- https://lore.kernel.org/git/20260109213021.2546-2-l.s.r@web.de/

Signed-off-by: Shreyansh Paliwal <shreyanshpaliwalcmsmn@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agocontrib/subtree: process out-of-prefix subtrees
Colin Stagner [Wed, 18 Feb 2026 02:31:32 +0000 (20:31 -0600)] 
contrib/subtree: process out-of-prefix subtrees

`should_ignore_subtree_split_commit` detects subtrees which are
outside of the current path --prefix and ignores them. This can
speed up splits of repositories that have many subtrees.

Since its inception [1], every iteration of this logic [2], [3]
incorrectly excludes commits. This alters the split history. The
split history and its commit hashes are API contract, so this is
not permissible.

While a commit from a different subtree may look like it doesn't
contribute anything to a split, sometimes it does. Merge commits
are a particular hot spot. For these, the pruning logic in
`copy_or_skip` performs:

1. a check for "treesame" parents
2. two different common ancestry checks

These checks operate on the **split history**, not the input
history. The split history omits commits that do not affect the
--prefix. This can significantly alter the ancestry of a merge.
In order to determine if `copy_or_skip` will skip a merge, it
is likely necessary to compute all the split history... which
is what `should_ignore_subtree_split_commit` tries to avoid.

To make this logic API-preserving, we could gate it behind a
new CLI argument. The present implementation is actually a
speed penalty in many cases, however, so this is not done here.

Remove the `should_ignore_subtree_split_commit` logic. This
fixes the regression reported in [4].

[1]: 98ba49ccc2 (subtree: fix split processing with multiple subtrees present, 2023-12-01)

[2]: 83f9dad7d6 (contrib/subtree: fix split with squashed subtrees, 2025-09-09)

[3]: 28a7e27cff (contrib/subtree: detect rewritten subtree commits, 2026-01-09)

[4]: <20251230170719.845029-1-george@mail.dietrich.pub>

Reported-by: George <george@mail.dietrich.pub>
Reported-by: Christian Heusel <christian@heusel.eu>
Signed-off-by: Colin Stagner <ask+git@howdoi.land>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agocontrib/subtree: test history depth
Colin Stagner [Wed, 18 Feb 2026 02:31:31 +0000 (20:31 -0600)] 
contrib/subtree: test history depth

Add history depth checks to some of the subtree unit tests.

These checks were previously introduced as part of 28a7e27cff
(contrib/subtree: detect rewritten subtree commits, 2026-01-09),
which has since been reverted.

Signed-off-by: Colin Stagner <ask+git@howdoi.land>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agocontrib/subtree: capture additional test-cases
Colin Stagner [Wed, 18 Feb 2026 02:31:30 +0000 (20:31 -0600)] 
contrib/subtree: capture additional test-cases

Patch series e7b07376e5 (Merge branch 'rs/subtree-fixes',
2018-10-26) corrects several defects in `git subtree split`.
The defects affect `split --rejoin` and merge commit processing.

There is no test coverage for this, and e7b07376e5 did not
introduce any.

Convert the minimum working example [1] from the original patch
submission [2] into test cases.

[1]: https://gist.github.com/FoxFireX/1b794384612b7fd5e7cd157cff96269e

[2]: <20180928183540.48968-1-roger.strain@swri.org>

Signed-off-by: Colin Stagner <ask+git@howdoi.land>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agoapply: normalize path in --directory argument
Joaquim Rocha [Wed, 18 Feb 2026 00:15:32 +0000 (00:15 +0000)] 
apply: normalize path in --directory argument

When passing a relative path like --directory=./some/sub, the leading
"./" caused apply to prepend it literally to patch filenames, resulting
in an error (invalid path).
There may be more cases like this where users pass some/./path to the
directory which can easily be normalized to an acceptable path, so
these changes try to normalize the path before using it.

Signed-off-by: Joaquim Rocha <joaquim@amutable.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agoMerge branch 'ps/for-each-ref-in-fixes' into ps/refs-for-each
Junio C Hamano [Fri, 20 Feb 2026 18:09:32 +0000 (10:09 -0800)] 
Merge branch 'ps/for-each-ref-in-fixes' into ps/refs-for-each

* ps/for-each-ref-in-fixes:
  bisect: simplify string_list memory handling
  bisect: fix misuse of `refs_for_each_ref_in()`
  pack-bitmap: fix bug with exact ref match in "pack.preferBitmapTips"
  pack-bitmap: deduplicate logic to iterate over preferred bitmap tips

7 weeks agoMerge branch 'lo/repo-info-keys' into lo/repo-leftover-bits
Junio C Hamano [Fri, 20 Feb 2026 17:17:56 +0000 (09:17 -0800)] 
Merge branch 'lo/repo-info-keys' into lo/repo-leftover-bits

* lo/repo-info-keys:
  repo: add new flag --keys to git-repo-info
  repo: rename the output format "keyvalue" to "lines"

7 weeks agoref-filter: clarify lstrip/rstrip component counting
Jeff King [Fri, 20 Feb 2026 06:00:03 +0000 (01:00 -0500)] 
ref-filter: clarify lstrip/rstrip component counting

When a strip option to the %(refname) placeholder is asked to leave N
path components, we first count up the path components to know how many
to remove. That happens with a loop like this:

/* Find total no of '/' separated path-components */
for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
;

which is a little hard to understand for two reasons.

First, the dereference in "*p++" is seemingly useless, since nobody
looks at the result. And static analyzers like Coverity will complain
about that. But removing the "*" will cause gcc to complain with
-Wint-conversion, since the two sides of the ternary do not match (one
is a pointer and the other an int).

Second, it is not clear what the meaning of "p" is at each iteration of
the loop, as its position with respect to our walk over the string
depends on how many slashes we've seen. The answer is that by itself, it
doesn't really mean anything: "p + i" represents the current state of
our walk, with "i" counting up slashes, and "p" by itself essentially
meaningless.

None of this behaves incorrectly, but ultimately the loop is just
counting the slashes in the refname. We can do that much more simply
with a for-loop iterating over the string and a separate slash counter.

We can also drop the comment, which is somewhat misleading. We are
counting slashes, not components (and a comment later in the function
makes it clear that we must add one to compensate). In the new code it
is obvious that we are counting slashes here.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agomailmap: drop global config variables
Burak Kaan Karaçay [Fri, 20 Feb 2026 06:04:42 +0000 (09:04 +0300)] 
mailmap: drop global config variables

The 'mailmap.file' and 'mailmap.blob' configurations are currently
parsed and stored in the global variables 'git_mailmap_file' and
'git_mailmap_blob'. Since these values are typically only needed once
when initializing a mailmap, there is no need to keep them as global
state throughout the lifetime of the Git process.

To reduce global state, remove these global variables and instead use
'repo_config_get_*' functions to read the configuration on demand.

Signed-off-by: Burak Kaan Karaçay <bkkaracay@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agomailmap: stop using the_repository
Burak Kaan Karaçay [Fri, 20 Feb 2026 06:04:41 +0000 (09:04 +0300)] 
mailmap: stop using the_repository

The 'read_mailmap' and 'read_mailmap_blob' functions rely on the global
'the_repository' variable. Update both functions to accept a
'struct repository' parameter.

Update all callers to pass 'the_repository' to retain the current
behavior.

Signed-off-by: Burak Kaan Karaçay <bkkaracay@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agoosxkeychain: define build targets in the top-level Makefile.
Koji Nakamaru [Fri, 20 Feb 2026 01:39:00 +0000 (01:39 +0000)] 
osxkeychain: define build targets in the top-level Makefile.

The fix for git-credential-osxkeychain in 4580bcd235 (osxkeychain: avoid
incorrectly skipping store operation, 2025-11-14) introduced linkage
with libgit.a, and its Makefile was adjusted accordingly. However, the
build fails as of 864f55e190 because several macOS-specific refinements
were applied to the top-level Makefile and config.mak.uname, such as:

  - 363837afe7 (macOS: make Homebrew use configurable, 2025-12-24)
  - cee341e9dd (macOS: use iconv from Homebrew if needed and present,
    2025-12-24)
  - d281241518 (utf8.c: enable workaround for iconv under macOS 14/15,
    2026-01-12)

Since libgit.a and its corresponding header files depend on many flags
defined in the top-level Makefile, these flags must be consistently
defined when building git-credential-osxkeychain. Continuing to manually
adjust the git-credential-osxkeychain Makefile is cumbersome and
fragile.

Define the build targets for git-credential-osxkeychain in the top-level
Makefile and modify its local Makefile to simply rely on those targets.

Helped-by: Junio C Hamano <gitster@pobox.com>
Reported-by: D. Ben Knoble <ben.knoble@gmail.com>
Helped-by: Kristoffer Haugsbakk <kristofferhaugsbakk@fastmail.com>
Signed-off-by: Koji Nakamaru <koji.nakamaru@gree.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agot6006: don't use iconv(1) without ICONV prereq
Patrick Steinhardt [Fri, 20 Feb 2026 08:26:03 +0000 (09:26 +0100)] 
t6006: don't use iconv(1) without ICONV prereq

Two tests in t6006 depend on the iconv(1) prerequisite to reencode a
commit message. This executable may not even exist though in case the
prereq is not set, which will cause the tests to fail.

Fix this by using UTF-8 instead when the prereq is not set.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agot5550: add ICONV prereq to tests that use "$HTTPD_URL/error"
Patrick Steinhardt [Fri, 20 Feb 2026 08:26:02 +0000 (09:26 +0100)] 
t5550: add ICONV prereq to tests that use "$HTTPD_URL/error"

We've got a bunch of tests in t5550 that connect to "$HTTPD_URL/error"
to ensure that error messages are properly forwarded. This URL executes
the "t/lib-httpd/error.sh" script, which in turn depends on the iconv(1)
executable to reencode the message.

This executable may not exist on platforms, which will make the tests
fail. Guard them with the ICONV prereq to fix such failures.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agot4205: improve handling of ICONV prerequisite
Patrick Steinhardt [Fri, 20 Feb 2026 08:26:01 +0000 (09:26 +0100)] 
t4205: improve handling of ICONV prerequisite

In t4205 we have a bunch of tests that depend on the iconv prereq. This
is for most of the part because we format commit messages that have been
encoded in an encoding different than UTF-8.

Those tests fall into two classes though:

  - One class of tests outputs the data as-is without reencoding.

  - One class of tests outputs the data with "i18n.logOutputEncoding" to
    reencode it.

Curiously enough, both of these classes are marked with the ICONV
prereq, even though one might expect that the first class wouldn't need
the prereq. This is because we unconditionally use ISO-8859-1 encoding
for the initial commit message, and thus we depend on converting to
UTF-8 indeed.

This creates another problem though: when the iconv(1) executable does
not exist the test setup fails, even in the case where the ICONV prereq
has not been set.

Fix these issues by making the test encoding conditional on ICONV: if
it's available we use ISO-8859-1, otherwise we use UTF-8. This fixes the
test setup on platforms without iconv(1), and it allows us to drop the
ICONV prereq from a bunch of tests.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agot40xx: don't use iconv(1) without ICONV prereq
Patrick Steinhardt [Fri, 20 Feb 2026 08:26:00 +0000 (09:26 +0100)] 
t40xx: don't use iconv(1) without ICONV prereq

We've got a couple of tests related to diffs in t40xx that use the
iconv(1) executable to convert the encoding of a commit message. All of
these tests are prepared to handle a missing ICONV prereq, in which case
they will simply use UTF-8 encoding.

But even if the ICONV prerequisite has failed we try to use the iconv(1)
executable, even though it's not safe to assume that the executable
exists in that case. And besides that, it's also unnecessary to use
iconv(1) in the first place, as we would only use it to convert from
UTF-8 to UTF-8, which should be equivalent to a no-op.

Fix the issue and skip the call to iconv(1) in case the prerequisite is
not set. This makes tests work on systems that don't have iconv at all.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agot: don't set ICONV prereq when iconv(1) is missing
Patrick Steinhardt [Fri, 20 Feb 2026 08:25:59 +0000 (09:25 +0100)] 
t: don't set ICONV prereq when iconv(1) is missing

We've got a couple of tests that exercise Git with different encodings,
typically around commit messages. All of these tests depend on the ICONV
prerequisite, which is set when Git was built with support for iconv.

Many of those tests also end up using the iconv(1) executable to
reencode text. But while tests can rely on the fact that Git does have
support for iconv, they cannot assume that the iconv(1) executable
exists. The consequence is thus that tests will break in case Git is
built with iconv, but the executable doesn't exist. In fact, some of the
tests even use the iconv(1) executable unconditionally, regardless of
whether or not the ICONV prerequisite is set.

Git for Windows has recently (unintentionally) shipped a change where
the iconv(1) binary is not getting installed anymore [1]. And as we use
Git for Windows directly in MSVC+Meson jobs in GitLab CI this has caused
such tests to break. The missing iconv(1) binary is considered a bug
that will be fixed in Git for Windows. But regardless of that it makes
sense to not assume the binary to always exist so that our test suite
passes on platforms that don't have iconv at all.

Extend the ICONV prerequisite so that we know to skip tests in case the
iconv(1) binary doesn't exist. We'll adapt tests that are currently
broken in subsequent commits.

[1]: https://github.com/git-for-windows/git/issues/6083

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agohook: add -z option to "git hook list"
Adrian Ratiu [Wed, 18 Feb 2026 22:23:52 +0000 (00:23 +0200)] 
hook: add -z option to "git hook list"

Add a NUL-terminate mode to git hook list, just in case hooks are
configured with weird characters like newlines in their names.

Suggested-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agohook: allow out-of-repo 'git hook' invocations
Emily Shaffer [Wed, 18 Feb 2026 22:23:51 +0000 (00:23 +0200)] 
hook: allow out-of-repo 'git hook' invocations

Since hooks can now be supplied via the config, and a config can be
present without a gitdir via the global and system configs, we can start
to allow 'git hook run' to occur without a gitdir. This enables us to do
things like run sendemail-validate hooks when running 'git send-email'
from a nongit directory.

It still doesn't make sense to look for hooks in the hookdir in nongit
repos, though, as there is no hookdir.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agohook: allow event = "" to overwrite previous values
Adrian Ratiu [Wed, 18 Feb 2026 22:23:50 +0000 (00:23 +0200)] 
hook: allow event = "" to overwrite previous values

Add the ability for empty events to clear previously set multivalue
variables, so the newly added "hook.*.event" behave like the other
multivalued keys.

Suggested-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agohook: allow disabling config hooks
Adrian Ratiu [Wed, 18 Feb 2026 22:23:49 +0000 (00:23 +0200)] 
hook: allow disabling config hooks

Hooks specified via configs are always enabled, however users
might want to disable them without removing from the config,
like locally disabling a global hook.

Add a hook.<name>.enabled config which defaults to true and
can be optionally set for each configured hook.

Suggested-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agohook: include hooks from the config
Adrian Ratiu [Wed, 18 Feb 2026 22:23:48 +0000 (00:23 +0200)] 
hook: include hooks from the config

Teach the hook.[hc] library to parse configs to populate the list of
hooks to run for a given event.

Multiple commands can be specified for a given hook by providing
"hook.<friendly-name>.command = <path-to-hook>" and
"hook.<friendly-name>.event = <hook-event>" lines.

Hooks will be started in config order of the "hook.<name>.event"
lines and will be run sequentially (.jobs == 1) like before.
Running the hooks in parallel will be enabled in a future patch.

The "traditional" hook from the hookdir is run last, if present.

A strmap cache is added to struct repository to avoid re-reading
the configs on each rook run. This is useful for hooks like the
ref-transaction which gets executed multiple times per process.

Examples:

  $ git config --get-regexp "^hook\."
  hook.bar.command=~/bar.sh
  hook.bar.event=pre-commit

  # Will run ~/bar.sh, then .git/hooks/pre-commit
  $ git hook run pre-commit

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
7 weeks agohook: add "git hook list" command
Emily Shaffer [Wed, 18 Feb 2026 22:23:47 +0000 (00:23 +0200)] 
hook: add "git hook list" command

The previous commit introduced an ability to run multiple commands for
hook events and next commit will introduce the ability to define hooks
from configs, in addition to the "traditional" hooks from the hookdir.

Introduce a new command "git hook list" to make inspecting hooks easier
both for users and for the tests we will add.

Further commits will expand on this, e.g. by adding a -z output mode.

Signed-off-by: Emily Shaffer <emilyshaffer@google.com>
Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>