]> git.ipfire.org Git - thirdparty/git.git/log
thirdparty/git.git
3 weeks agopath-walk: support `tree:0` filter
Taylor Blau [Fri, 22 May 2026 18:24:35 +0000 (18:24 +0000)] 
path-walk: support `tree:0` filter

The `tree:0` object filter omits all trees and blobs from the result,
keeping only commits and tags. Consequently, this filter type should
has a fairly straightforward integration with path-walk, as the decision
to include an object depends only on its type and does not depend on any
path-sensitive state.

Mapping it onto `path_walk_info` is direct: set `info->trees = 0` and
`info->blobs = 0` in `prepare_filters()` when the `LOFC_TREE_DEPTH`
choice is requested with depth zero. The existing code already plumbs
those flags through the rest of the walk:

 - 'walk_objects_by_path()' sets `revs->blob_objects = info->blobs` and
   `revs->tree_objects = info->trees` before `prepare_revision_walk()`,
   so the revision walk doesn't try to enumerate trees or blobs itself.

 - The commit-walk loop short-circuits the root-tree fetch with
   "if (!info->trees && !info->blobs) continue;", so we never even
   look up the root tree, let alone descend into it.

 - `setup_pending_objects()` skips pending trees and blobs based on
   the same flags.

This means the path-walk doesn't allocate or expand any tree structures
at all under `tree:0`, which matches the intended behavior of the
filter.

However, this requires first fixing some issues with how the path-walk
API handles directly-requested trees _and_ trees requested through
lightweight tags. These changes create substantial updates to
t6601-path-walk.sh, which the previous change highlighted as a problem
by tagging otherwise-unreachable trees and having them not appear in the
output.

Non-zero tree-depth filters are not supported. Those depend on the depth
at which a tree is visited, which is a path-walk concept the filter
machinery doesn't currently share with the path-walk API. Reject them in
`prepare_filters()` with a helpful error and let pack-objects fall back
to the regular traversal, the same way it already does for unsupported
filters.

Add coverage in t6601 for both `--all` and a single-branch case to
confirm that no trees or blobs are emitted, and a separate test that
`tree:1` is rejected with the expected error message. Place the new
tests before "setup sparse filter blob" so they run on the original set
of refs, before the orphan branch that the sparse-tree tests create.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agot6601: tag otherwise-unreachable trees
Derrick Stolee [Fri, 22 May 2026 18:24:34 +0000 (18:24 +0000)] 
t6601: tag otherwise-unreachable trees

The tests in t6601-path-walk.sh demonstrate the behavior of the
path-walk API under different conditions. One thing that I noticed while
updating the behavior of directly-requested objects is that we don't
actually emit tagged trees. This was previously not noticed due to those
tagged trees actually being reachable from commits that we are including
in the path-walk.

Update the test setup to have tree-tag and tree-tag2 point to trees that
are otherwise unreachable.

It is worth noting that this does not meaningfully change any of the
other test cases, demontrating the bug.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agopack-objects: support sparse:oid filter with path-walk
Derrick Stolee [Fri, 22 May 2026 18:24:33 +0000 (18:24 +0000)] 
pack-objects: support sparse:oid filter with path-walk

The --filter=sparse:<oid> option to 'git pack-objects' allows focusing
an object set to a sparse-checkout definition. This reduces the set of
matching blobs while retaining all reachable trees. No server currently
supports fetching with this filter because it is expensive to compute
and reachability bitmaps do not help without a significant effort to
extend the bitmap feature to store bitmaps for each supported sparse-
checkout definition.

Without focusing on serving fetches and clones with these filters, there
are still benefits that could be realized by making this faster. With
the sparse index, it's more realistic now than ever to be able to
operate a local clone that was bootstrapped by a packfile created with
a sparse filter, because the missing trees are not needed to move a
sparse-checkout from one commit to another or to view the history of any
path in scope. Such clones could perhaps be bootstrapped by partial
bundles.

Previously, constructing these sparse packs has been incredibly
computationally inefficient. The revision walk that explores which
objects are in scope spends a lot of time checking each object to see if
it matches the sparse-checkout patterns, causing quadratic behavior
(number of objects times number of sparse-checkout patterns). This
improves somewhat when using cone-mode sparse-checkout patterns that can
use hashtables and prefix matches to determine containment. However, the
check per object is still too expensive for most cases.

This is where the path-walk feature comes in. We can proceed as normal
by placing objects in bins by path and _then_ check a group of objects
all at once. Since sparse:<oid> only restricts blobs, the path-walk must
include all reachable trees while using the cone-mode patterns to skip
blobs at paths outside the sparse scope. This establishes a baseline for
a potential future "treesparse:<oid>" filter that would also restrict
trees, but introducing such a new filter is deferred to a later change.

The implementation here is focused around loading the sparse-checkout
patterns from the provided object ID and checking that the patterns are
indeed cone-mode patterns. We can then load the correct pattern list
into the path walk context and use the logic that already exists from
bff45557675 (backfill: add --sparse option, 2025-02-03), though that
feature loads sparse-checkout patterns from the worktree's local
settings and also restricts tree objects. We use a combination of errors
and warnings to signal problems during this load. The difference is that
errors are likely fatal for the non-path-walk version while the warnings
are probably just implementation details for the path-walk version and
the 'git pack-objects' command can fall back to the revision walk
version.

Now that the SEEN flag is deferred until after pattern checks (from the
previous commit), handle the case where a tree with a shared OID appears
at both an out-of-cone and in-cone path. When trees are not being pruned
(pl_sparse_trees == 0), the path-walk re-walks the tree at the in-cone
path so that in-cone blobs within it are discovered. The new tests in
t5317 and t6601 demonstrate this behavior and would fail without these
changes.

The performance test p5315 shows the impact of this change when using
sparse filters:

Test                                              HEAD~1     HEAD
----------------------------------------------------------------------
5315.10: repack (sparse:oid)                      77.98    77.47  -0.7%
5315.11: repack size (sparse:oid)                187.5M   187.4M  -0.0%
5315.12: repack (sparse:oid, --path-walk)         77.91    31.41 -59.7%
5315.13: repack size (sparse:oid, --path-walk)   187.5M   161.1M -14.1%

These performance tests were run on the Git repository. The --path-walk
feature shows meaningful space savings (14% smaller for sparse packs)
and dramatic time savings (60% faster) by leveraging the path-walk's
ability to skip blobs outside the sparse scope.

Co-authored-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Taylor Blaue <me@ttaylorr.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agopath-walk: add pl_sparse_trees to control tree pruning
Derrick Stolee [Fri, 22 May 2026 18:24:32 +0000 (18:24 +0000)] 
path-walk: add pl_sparse_trees to control tree pruning

The path-walk API prunes trees and blobs when a sparse-checkout pattern
list is provided, which is the correct behavior for 'git backfill
--sparse' since it only needs to fill in objects at paths within the
sparse cone.

However, a future change will use the path-walk API with a sparse:<oid>
filter that restricts only blobs while retaining all reachable trees.
To support both behaviors, add a 'pl_sparse_trees' flag to
path_walk_info. When set (as in 'git backfill --sparse' and the
--stdin-pl test helper mode), the sparse patterns prune both trees and
blobs. When unset, only blobs are filtered and all trees are walked and
reported.

Additionally, move the SEEN flag assignment in add_tree_entries() to
after the sparse pattern and pathspec checks. Previously, SEEN was set
immediately upon discovering an object, before checking whether its path
matched the sparse patterns. When the same object ID appeared at
multiple paths (e.g. sibling directories with identical contents), the
first path to be visited would mark the object as SEEN. If that path was
outside the sparse cone, the object would be skipped there but also
never discovered at its in-cone path.

By deferring the SEEN flag until after the checks pass, objects that are
skipped due to sparse filtering remain discoverable at other paths where
they may be in scope.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agopath-walk: support blob size limit filter
Derrick Stolee [Fri, 22 May 2026 18:24:31 +0000 (18:24 +0000)] 
path-walk: support blob size limit filter

Extend the path-walk API to handle the 'blob:limit=<size>' object
filter natively. This filter omits blobs whose size is equal to or
greater than the given limit, matching the semantics used by the
list-objects-filter machinery.

When revs->filter.choice is LOFC_BLOB_LIMIT, the prepare_filters()
method stores the limit value in info->blob_limit and clears the filter
from revs. If the limit is zero, this degenerates to blob:none (all
blobs excluded), so info->blobs is set to 0 instead.

During walk_path(), blob batches are filtered before being delivered to
the callback: each blob's size is checked via odb_read_object_info(),
and only blobs strictly smaller than the limit are included. Blobs whose
size cannot be determined (e.g. missing in a partial clone) are
conservatively included, matching the existing filter behavior. Empty
batches after filtering are skipped entirely.

The check for inclusion in the path batch looks a little strange at
first glance. We use odb_read_object_info() to read the object's size.
Based on all of the assumptions to this point, this _should_ return
OBJ_BLOB. Since we are focused on the size filter, we use a
short-circuited OR (||) to skip the size check if that method returns a
different object type.

Notice that this inspection of object sizes requires the content to be
present in the repository. The odb_read_object_info() call will download
a missing blob on-demand. This means that the use of the path-walk API
within 'git backfill' would not operate nicely with this filter type.
The intention of that command is to download missing blobs in batches.
Downloading objects one-by-one would go against the point. Update the
validation in 'git backfill' to add its own compatibility check on top
of path_walk_filter_compatible().

Add tests for blob:limit=0 (equivalent to blob:none) and blob:limit=3
(which exercises partial filtering within a batch where some blobs are
kept and others are excluded).

Co-authored-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agobackfill: die on incompatible filter options
Derrick Stolee [Fri, 22 May 2026 18:24:30 +0000 (18:24 +0000)] 
backfill: die on incompatible filter options

The 'git backfill' command uses the path-walk API in a critical way: it
uses the objects output from the command to find the batches of missing
objects that should be requested from the server. Unlike 'git
pack-objects', we cannot fall back to another mechanism.

The previous change added the path_walk_filter_compatible() method that
we can reuse here. Use it during argument validation in cmd_backfill().

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agopath-walk: support blobless filter
Derrick Stolee [Fri, 22 May 2026 18:24:29 +0000 (18:24 +0000)] 
path-walk: support blobless filter

The 'git pack-objects' command can opt-in to using the path-walk API for
scanning the objects. Currently, this option is dynamically disabled if
combined with '--filter=<X>', even when using a simple filter such as
'blob:none' to signal a blobless packfile. This is a common scenario for
repos at scale, so is worth integrating.

Also, users can opt-in to the '--path-walk' option by default through
the pack.usePathWalk=true config option. When using that in a blobless
partial clone, the following warning can appear even though the user did
not specify either option directly:

  warning: cannot use --filter with --path-walk

Teach the path-walk API to handle the 'blob:none' object filter
natively. When revs->filter.choice is LOFC_BLOB_NONE, the path-walk
sets info->blobs to 0 (skipping all blob objects) and clears the
filter from revs so that prepare_revision_walk() does not reject the
configuration.

This check is implemented in the static prepare_filters() method, which
will simultaneously check if the input filters are compatible and will
make the appropriate mutations to the path_walk_info and filters if the
path_walk_info is non-NULL. This allows us to use this logic both in the
API method path_walk_filter_compatible() for use in
builtin/pack-objects.c and as a prep step in walk_objects_by_path().

Update the test helper (test-path-walk) to accept --filter=<spec>
as a test-tool option (before '--'), applying it to revs after
setup_revisions() to avoid the --objects requirement check. We can also
revert recent GIT_TEST_PACK_PATH_WALK overrides in t5620.

Also switch test-path-walk from REV_INFO_INIT with manual repo
assignment to repo_init_revisions(), which properly initializes
the filter_spec strbuf needed for filter parsing.

Add tests for blob:none with --all and with a single branch.

The performance test p5315 shows the impact of this change when using
blobless filters:

Test                                           HEAD~1     HEAD
---------------------------------------------------------------------
5315.6: repack (blob:none)                      13.53   13.87  +2.5%
5315.7: repack size (blob:none)                137.7M  137.8M  +0.1%
5315.8: repack (blob:none, --path-walk)         13.51   23.43 +73.4%
5315.9: repack size (blob:none, --path-walk)   137.7M  115.2M -16.3%

These performance tests were run on the Git repository. The --path-walk
feature shows meaningful space savings (16% smaller for blobless packs)
at the cost of increased computation time due to the two compression
passes. This data demonstrates that the feature is engaged and provides
real compression benefits when --no-reuse-delta forces fresh deltas.

Co-Authored-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agopath-walk: always emit directly-requested objects
Derrick Stolee [Fri, 22 May 2026 18:24:28 +0000 (18:24 +0000)] 
path-walk: always emit directly-requested objects

We are preparing to integrate the path-walk API with some --filter options
in 'git pack-objects', but there is a subtle issue that is revealed when
those are put together and the test suite is run with
GIT_TEST_PACK_PATH_WALK=1.

When a filter reduces the set of requested objects, this results in
filtering out directly-requested objects, such as in the download of needed
blobs in a blobless partial clone.

The root cause is that the scan of pending objects in the path-walk API
respects the filters set in the path_walk_info instead of overriding them
for pending objects.

We can tell that a path is part of the directly-referenced objects if its
path name starts with '/' (other paths, including root trees never have this
starting character). Create a path_is_for_direct_objects() to make this
meaning clear, especially as we add more references in the future as we
integrate the path-walk API with partial clone filter options.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agot/perf: add pack-objects filter and path-walk benchmark
Derrick Stolee [Fri, 22 May 2026 18:24:27 +0000 (18:24 +0000)] 
t/perf: add pack-objects filter and path-walk benchmark

Add p5315-pack-objects-filter.sh to measure the performance of
'git pack-objects --revs --all' under different filter and traversal
combinations:

 * no filter (baseline)
 * --filter=blob:none (blobless)
 * --filter=sparse:oid=<oid> (cone-mode sparse)

Each filter scenario is tested both with and without --path-walk,
producing paired measurements that show the impact of the path-walk
traversal for each filter type as we integrate the --path-walk feature
with different --filter options. It currently has no integration so
falls back to the standard revision walk. Thus, there are no significant
differences in the current results other than a full repack (and even
then, the --path-walk feature is not incredibly different for the
default Git repository):

Test                                             HEAD
-----------------------------------------------------
5315.2: repack (no filter)                      27.91
5315.3: repack size (no filter)                250.7M
5315.4: repack (no filter, --path-walk)         34.92
5315.5: repack size (no filter, --path-walk)   220.0M
5315.6: repack (blob:none)                      13.63
5315.7: repack size (blob:none)                137.6M
5315.8: repack (blob:none, --path-walk)         13.48
5315.9: repack size (blob:none, --path-walk)   137.7M
5315.10: repack (sparse:oid)                    72.67
5315.11: repack size (sparse:oid)              187.4M
5315.12: repack (sparse:oid, --path-walk)       72.47
5315.13: repack size (sparse:oid, --path-walk) 187.4M

The sparse filter definition is built automatically by sampling
depth-2 directories from the test repository, making the test work
on any repo passed via GIT_PERF_LARGE_REPO. For repos that lack
depth-2 directories, a single top-level directory is used; for flat
repos, the sparse tests are skipped via prerequisite.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agopack-objects: pass --objects with --path-walk
Derrick Stolee [Fri, 22 May 2026 18:24:26 +0000 (18:24 +0000)] 
pack-objects: pass --objects with --path-walk

When 'git pack-objects' has the --path-walk option enabled, it uses a
different set of revision walk parameters than normal. For one,
--objects was previously assumed by the path-walk API and could be
omitted. We also needed --boundary to allow discovering UNINTERESTING
objects to use as delta bases.

We will be updating the path-walk API soon to work with some filter
options. However, the revision machinery will trigger a fatal error:

  fatal: object filtering requires --objects

The fix is easy: add the --objects option as an argument. This has no
effect on the path-walk API but does simplify the revision option
parsing for the objects filter.

We can remove the comment about "removing" the options because they were
never removed and instead not added. We still need to disable using
bitmaps.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agot5620: make test work with path-walk var
Derrick Stolee [Fri, 22 May 2026 18:24:25 +0000 (18:24 +0000)] 
t5620: make test work with path-walk var

The GIT_TEST_PACK_PATH_WALK test variable allows enabling the
--path-walk option to 'git pack-objects' by default. This sometimes
engages the warning that --path-walk is incompatible with the --filter
option. These tests in t5620 fail due to this warning over stderr in
this case. Disable this variable for this moment until these options
work together.

Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agotransport-helper: fix typo in BUG() message
Jeff King [Fri, 22 May 2026 04:43:52 +0000 (00:43 -0400)] 
transport-helper: fix typo in BUG() message

We mistakenly refer to the git_connect_service enum as "_type" rather
than "_service". Users should never see this message in practice, but it
is slightly confusing when reading the code.

Reported-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agodoc: hook: don’t self-link via config include
Kristoffer Haugsbakk [Thu, 21 May 2026 16:25:58 +0000 (18:25 +0200)] 
doc: hook: don’t self-link via config include

Do not link to git-hook(1) from the config options when we already are
in that doc.

This implementation is similar to the updates to git-init(1) and
git-commit(1), implemented in [1] and [2], respectively.

† 1: e7b3a768 (doc: git-init: rework config item init.templateDir,
     2024-03-10)
† 2: 819fdd6e (doc: convert git commit config to new format, 2025-01-15)

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agodoc: config: include existing git-hook(1) section
Kristoffer Haugsbakk [Thu, 21 May 2026 16:25:57 +0000 (18:25 +0200)] 
doc: config: include existing git-hook(1) section

It is already included in git-hook(1) but missing from git-config(1).

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agodoc: hook: consistently capitalize Git
Kristoffer Haugsbakk [Thu, 21 May 2026 16:25:56 +0000 (18:25 +0200)] 
doc: hook: consistently capitalize Git

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agodoc: hook: remove stray backtick
Kristoffer Haugsbakk [Thu, 21 May 2026 16:25:55 +0000 (18:25 +0200)] 
doc: hook: remove stray backtick

Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoMerge branch 'ps/setup-wo-the-repository' into ps/setup-centralize-odb-creation
Junio C Hamano [Thu, 21 May 2026 13:37:12 +0000 (22:37 +0900)] 
Merge branch 'ps/setup-wo-the-repository' into ps/setup-centralize-odb-creation

* ps/setup-wo-the-repository:
  setup: stop using `the_repository` in `init_db()`
  setup: stop using `the_repository` in `create_reference_database()`
  setup: stop using `the_repository` in `initialize_repository_version()`
  setup: stop using `the_repository` in `check_repository_format()`
  setup: stop using `the_repository` in `upgrade_repository_format()`
  setup: stop using `the_repository` in `setup_git_directory()`
  setup: stop using `the_repository` in `setup_git_directory_gently()`
  setup: stop using `the_repository` in `setup_git_env()`
  setup: stop using `the_repository` in `set_git_work_tree()`
  setup: stop using `the_repository` in `setup_work_tree()`
  setup: stop using `the_repository` in `enter_repo()`
  setup: stop using `the_repository` in `verify_non_filename()`
  setup: stop using `the_repository` in `verify_filename()`
  setup: stop using `the_repository` in `path_inside_repo()`
  setup: stop using `the_repository` in `prefix_path()`
  setup: stop using `the_repository` in `is_inside_work_tree()`
  setup: stop using `the_repository` in `is_inside_git_dir()`
  setup: replace use of `the_repository` in static functions

3 weeks agoThe 7th batch
Junio C Hamano [Thu, 21 May 2026 23:47:06 +0000 (08:47 +0900)] 
The 7th batch

With this batch, we have flushed all the topics that need to
be merged to 'maint' to make its build healthy.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoMerge branch 'ps/maintenance-daemonize-lockfix'
Junio C Hamano [Thu, 21 May 2026 23:48:20 +0000 (08:48 +0900)] 
Merge branch 'ps/maintenance-daemonize-lockfix'

"git maintenance" that goes background did not use the lockfile to
prevent multiple maintenance processes from running at the same
time, which has been corrected.

* ps/maintenance-daemonize-lockfix:
  run-command: honor "gc.auto" for auto-maintenance
  builtin/maintenance: fix locking with "--detach"

3 weeks agoMerge branch 'jk/apply-leakfix'
Junio C Hamano [Thu, 21 May 2026 23:48:20 +0000 (08:48 +0900)] 
Merge branch 'jk/apply-leakfix'

Leakfix.

* jk/apply-leakfix:
  apply: plug leak on "patch too large" error

3 weeks agoMerge branch 'jk/commit-sign-overflow-fix'
Junio C Hamano [Thu, 21 May 2026 23:48:20 +0000 (08:48 +0900)] 
Merge branch 'jk/commit-sign-overflow-fix'

Leakfix.

* jk/commit-sign-overflow-fix:
  commit: handle large commit messages in utf8 verification

3 weeks agogit-jump: pick a mode automatically when invoked without arguments
Greg Hurrell [Thu, 21 May 2026 13:45:09 +0000 (13:45 +0000)] 
git-jump: pick a mode automatically when invoked without arguments

When `git jump` is invoked with no positional arguments (and no
arguments after `--stdout`) it currently prints usage and exits with
status 1.

But there are two situations where we can usefully infer the most
valuable and likely mode that a user would want to use, and select it
automatically:

1. When there are unmerged paths in the index, the user likely
   wants `git jump merge`.

2. When the working tree has unstaged changes, the user likely
   wants `git jump diff`.

In this commit we teach `git jump` a new "auto" mode which detects these
cases and dispatches to the corresponding mode automatically. The user
can either explicitly spell out `git jump auto`, or just leave it at
`git jump` (because "auto" is the default).

If none of the interesting cases listed above applies, then auto mode
falls back to the existing usage-and-exit behavior.

Signed-off-by: Greg Hurrell <greg.hurrell@datadoghq.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoMerge branch 'ps/odb-in-memory' into ps/odb-source-loose
Junio C Hamano [Thu, 21 May 2026 13:34:55 +0000 (22:34 +0900)] 
Merge branch 'ps/odb-in-memory' into ps/odb-source-loose

* ps/odb-in-memory: (24 commits)
  t/unit-tests: add tests for the in-memory object source
  odb: generic in-memory source
  odb/source-inmemory: stub out remaining functions
  odb/source-inmemory: implement `freshen_object()` callback
  odb/source-inmemory: implement `count_objects()` callback
  odb/source-inmemory: implement `find_abbrev_len()` callback
  odb/source-inmemory: implement `for_each_object()` callback
  odb/source-inmemory: convert to use oidtree
  oidtree: add ability to store data
  cbtree: allow using arbitrary wrapper structures for nodes
  odb/source-inmemory: implement `write_object_stream()` callback
  odb/source-inmemory: implement `write_object()` callback
  odb/source-inmemory: implement `read_object_stream()` callback
  odb/source-inmemory: implement `read_object_info()` callback
  odb: fix unnecessary call to `find_cached_object()`
  odb/source-inmemory: implement `free()` callback
  odb: introduce "in-memory" source
  odb/transaction: make `write_object_stream()` pluggable
  object-file: generalize packfile writes to use odb_write_stream
  object-file: avoid fd seekback by checking object size upfront
  ...

3 weeks agogitlab-ci: update macOS image
Patrick Steinhardt [Thu, 21 May 2026 08:59:25 +0000 (10:59 +0200)] 
gitlab-ci: update macOS image

The GitLab CI jobs for macOS are all using the macOS 15 images. While
these images are not deprecated yet, there is a new image for macOS 26
generally available by now [1].

Switch two of our jobs to use the new image. The third job still
continues to use the old image. This ensures broader test coverage until
this old image gets deprecated.

[1]: https://docs.gitlab.com/ci/runners/hosted_runners/macos/

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agogitlab-ci: upgrade macOS runners
Patrick Steinhardt [Thu, 21 May 2026 08:59:24 +0000 (10:59 +0200)] 
gitlab-ci: upgrade macOS runners

We're currently using M1-based runners for our macOS jobs. GitLab has
since introduced a new M2 Pro-based runner type that is available for
all GitLab tiers [1], which upgrades from 4 to 6 cores and from 8 to 16
GB RAM.

Upgrade to this new runner type, which results in some nice speedups:

  - osx-clang goes from 26 minutes to 16 minutes.

  - osx-meson goes from 19 minutes to 13 minutes.

  - osx-reftable goes from 23 minutes to 14 mintues.

[1]: https://docs.gitlab.com/ci/runners/hosted_runners/macos/

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoapproxidate: use deferred mday adjustments for "specials"
Tuomas Ahola [Thu, 21 May 2026 10:54:08 +0000 (13:54 +0300)] 
approxidate: use deferred mday adjustments for "specials"

There are cases where the "wrap-to-yesterday" behavior of "tea" and
"noon" should be reverted later on down the line, so that "today tea"
and "tea today" won't yield different results.  However, the logic of
approxidate doesn't seem to lend itself particularly well to
such cases.

Start tackling the issue by reusing negative values of `tm->tm_mday`
field for deferred date adjustments which can be easily reverted, so
that the default logic of the special formats only applies if we don't
get any explicit date (mday) specification.  In particular, overwrite
the field with -1 in "today" and "yesterday", so that those formats will
be relative to the current date.  That makes specifications like "tea
yesterday" behave more sensibly: instead of going backwards to the
last tea-time and then a day back, Git will now understand that as the
tea-time of yesterday.

Replace the call of `update_tm()` in `date_time()` with the assignment
`tm->tm_mday = -2`.  Add the corresponding code to handle that in
`update_tm()`, wrapping to the previous day if the field still holds
such assignment, meaning that we haven't seen any better specification
for the day-of-month.  On the other hand, `mday=-3` would mean going
two days back and so on.  Even though such functionality isn't
actually needed by this patch, it won't add much complexity in the
code and is rather natural way to handle such values.

As `date_time()` won't no longer need the `now` struct, mark the
associated function parameters as unused.  The parameters themselves
have to stay, however, as those functions are called through pointers
in `approxidate_alpha`.  Add relevant tests to cover the changes.

Signed-off-by: Tuomas Ahola <taahol@utu.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoapproxidate: make "specials" respect fixed day-of-month
Tuomas Ahola [Thu, 21 May 2026 10:54:07 +0000 (13:54 +0300)] 
approxidate: make "specials" respect fixed day-of-month

The special approxidate time formats, "noon" and "tea" differ from
"12pm" and "5pm" by having the feature of wrapping to the previous day
if the current time is before those hours:

now  -> 2026-05-13 11:00:00 +0000

12pm -> 2026-05-13 12:00:00 +0000
5pm  -> 2026-05-13 17:00:00 +0000

noon -> 2026-05-12 12:00:00 +0000
tea  -> 2026-05-12 17:00:00 +0000

However, that logic carries too far.  Even when the date is specified,
the behavior of the "specials" depends on the current time.  Assuming
the same time as above, we get:

today at noon -> 2026-05-12 12:00:00 +0000 (should be 13 May)
13 May at tea -> 2026-05-12 17:00:00 +0000

or, using an example mentioned in date-formats.adoc:

last Friday at noon -> 2026-05-07 12:00:00 +0000 (should be 8 May)

The quirk seems to be rather old.  Already in 2006, Linus Torvalds
remarked that the date yielded by "one year ago yesterday at tea-time"
was "just silly and not even correct".  Indeed, even today it gives:

One year ago yesterday at tea-time -> 2025-05-11 17:00:00 +0000
  (should be 12 May)

Let's fix all of those with a simple patch.  Check whether we already
have a specified day-of-month in `tm->tm_mday` and make `date_time()`
stick to it.  Ensure the correct behavior with relevant tests.

Links:
  1. https://lore.kernel.org/git/Pine.LNX.4.64.0610101102560.3952@g5.osdl.org/

Signed-off-by: Tuomas Ahola <taahol@utu.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agot0006: add support for approxidate test date adjustment
Tuomas Ahola [Thu, 21 May 2026 10:54:06 +0000 (13:54 +0300)] 
t0006: add support for approxidate test date adjustment

t0006 uses a hard-coded test date and provides no convenient
way to override it temporarily.  Add an optional parameter to
check_approxidate to adjust the time as needed, and demonstrate
the feature with a new test.

Signed-off-by: Tuomas Ahola <taahol@utu.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoapproxidate: make "today" wrap to midnight
Tuomas Ahola [Thu, 21 May 2026 10:54:05 +0000 (13:54 +0300)] 
approxidate: make "today" wrap to midnight

Although some commands do reject invalid approxidate expressions,
in other cases those are simply evaluated as the current time.
Oftentimes that is a perfectly good compromise to handle silly
requests, but it isn't without rough edges.

Because of the silent acceptance, it is easy to forget that
"today" isn't actually a valid approxidate format.  That is
a bit awkward because while the fallback logic of using the
current time does make some sense, there is no deliberative
decision behind such behavior of "today".  Indeed, whatever
(non-)action "today" currently has, is just an accidental
side effect.

That means "git log --since=today" is currently unlikely to
print anything at all as it tries to list commits dated with
*future* timestamps.  Arguably it would be more useful to
list the commits of the current day---i.e. those made since
midnight.

On the other hand, "git log --until=today" doesn't really
filter commits at all.  Changing the definition of "today"
would make it return the commits made before the current day.
That isn't without problems though---running "git log
--until=today" in the late afternoon could reasonably include
the work done earlier that day (as the command currently
does do).

Still the utility of no-op "--until=today" is debatable and
perhaps outweighed by the pros of having "--since=today" to
mean "--since=midnight".  The thing is that the approxidate
machinery doesn't know about its consumers, so the meaning
of "today" has to be the same for "--since" and "--until".

In fact, "git log --until=" is documented as

`--until=<date>`::
`--before=<date>`::
Show commits older than _<date>_,

so excluding commits made today would actually match the
documentation more closely.

Moreover, a revision parameter "@{today}" is currently outright
rejected.  Making "today" a valid approxidate time format could
make a natural way to specify the state of the ref at the start
of the current day.

Bind "today" to new function `date_today()` as an approxidate
special.  Make it return the last midnight if no specific time
is given; i.e. retain the old behavior of "noon today" and such.

Document the new behavior of "git log --since=today" in
rev-list-options.adoc.

Signed-off-by: Tuomas Ahola <taahol@utu.fi>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoDocumentation/git-range-diff: add missing notes options in synopsis
Siddh Raman Pant [Thu, 21 May 2026 05:28:41 +0000 (10:58 +0530)] 
Documentation/git-range-diff: add missing notes options in synopsis

git-range-diff supports note options which are also mentioned later in
the help, but they are missing from the synopsis. Let's fix that.

Signed-off-by: Siddh Raman Pant <siddh.raman.pant@oracle.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoSync with 'maint'
Junio C Hamano [Thu, 21 May 2026 03:45:24 +0000 (12:45 +0900)] 
Sync with 'maint'

3 weeks agoStart preparing for 2.54.1 maint
Junio C Hamano [Thu, 21 May 2026 03:40:38 +0000 (12:40 +0900)] 
Start preparing for 2.54.1

Mostly build and CI related updates taken from the 'master' front
are included in here.

We still need to grab a couple more topics once they graduate to
'master', namely

    jk/apply-leakfix
    jk/commit-sign-overflow-fix

Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoMerge branch 'jc/t5551-fix-expensive' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:30:40 +0000 (12:30 +0900)] 
Merge branch 'jc/t5551-fix-expensive' into maint-2.54

Test fix.

* jc/t5551-fix-expensive:
  t5551: "GIT_TEST_LONG=Yes make test" is broken

3 weeks agoMerge branch 'jh/alias-i18n-fixes' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:30:11 +0000 (12:30 +0900)] 
Merge branch 'jh/alias-i18n-fixes' into maint-2.54

Further update to the i18n alias support to avoid regressions.

* jh/alias-i18n-fixes:
  alias: restore support for simple dotted aliases

3 weeks agoMerge branch 'js/mingw-no-nedmalloc' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:28:55 +0000 (12:28 +0900)] 
Merge branch 'js/mingw-no-nedmalloc' into maint-2.54

Stop using unmaintained custom allocator in Windows build which was
the last user of the code.

* js/mingw-no-nedmalloc:
  mingw: remove the vendored compat/nedmalloc/ subtree
  mingw: drop the build-system plumbing for nedmalloc
  mingw: stop using nedmalloc

3 weeks agoMerge branch 'js/maintenance-fix-deadlock-on-win10' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:27:47 +0000 (12:27 +0900)] 
Merge branch 'js/maintenance-fix-deadlock-on-win10' into maint-2.54

To help Windows 10 installations, avoid removing files whose
contents are still mmap()'ed.

* js/maintenance-fix-deadlock-on-win10:
  maintenance(geometric): do release the `.idx` files before repacking
  mingw: optionally use legacy (non-POSIX) delete semantics

3 weeks agoMerge branch 'js/t5564-socks-use-short-path' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:27:19 +0000 (12:27 +0900)] 
Merge branch 'js/t5564-socks-use-short-path' into maint-2.54

Avoid hitting the pathname limit for socks proxy socket during the
test.

* js/t5564-socks-use-short-path:
  t5564: use a short path for the SOCKS proxy socket

3 weeks agoMerge branch 'js/ci-github-actions-update' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:26:28 +0000 (12:26 +0900)] 
Merge branch 'js/ci-github-actions-update' into maint-2.54

Update various GitHub Actions versions.

* js/ci-github-actions-update:
  l10n: bump mshick/add-pr-comment from v2 to v3
  ci: bump git-for-windows/setup-git-for-windows-sdk from v1 to v2
  ci: bump actions/checkout from v5 to v6
  ci: bump actions/github-script from v8 to v9
  ci: bump actions/{upload,download}-artifact to v7 and v8
  ci: bump microsoft/setup-msbuild from v2 to v3

3 weeks agoMerge branch 'jk/revert-aa-reap-transport-child-processes' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:25:55 +0000 (12:25 +0900)] 
Merge branch 'jk/revert-aa-reap-transport-child-processes' into maint-2.54

Revert a recent change that introduced a regression to help mksh users.

* jk/revert-aa-reap-transport-child-processes:
  Revert "transport-helper, connect: use clean_on_exit to reap children on abnormal exit"

3 weeks agoMerge branch 'ps/clang-w-glibc-2.43-and-_Generic' into maint-2.54
Junio C Hamano [Thu, 21 May 2026 03:23:50 +0000 (12:23 +0900)] 
Merge branch 'ps/clang-w-glibc-2.43-and-_Generic' into maint-2.54

Headers from glibc 2.43 when used with clang does not allow
disabling C11 language features, causing build failures..

* ps/clang-w-glibc-2.43-and-_Generic:
  build: tolerate use of _Generic from glibc 2.43 with Clang

3 weeks agoThe 6th batch
Junio C Hamano [Thu, 21 May 2026 03:05:44 +0000 (12:05 +0900)] 
The 6th batch

Signed-off-by: Junio C Hamano <gitster@pobox.com>
3 weeks agoMerge branch 'sp/shallow-deepen-on-non-shallow-repo-fix'
Junio C Hamano [Thu, 21 May 2026 03:06:49 +0000 (12:06 +0900)] 
Merge branch 'sp/shallow-deepen-on-non-shallow-repo-fix'

"git fetch --deepen=<n>" in a full clone truncated the history to <n>
commits deep, which has been corrected to be a no-op instead.

* sp/shallow-deepen-on-non-shallow-repo-fix:
  shallow: fix relative deepen on non-shallow repositories

3 weeks agoMerge branch 'jc/ci-enable-expensive'
Junio C Hamano [Thu, 21 May 2026 03:06:48 +0000 (12:06 +0900)] 
Merge branch 'jc/ci-enable-expensive'

Enable expensive tests to catch topics that may cause breakages on
integration branches closer to their origin in the contributor PR
builds.

* jc/ci-enable-expensive:
  ci: enable EXPENSIVE for contributor builds

3 weeks agoMerge branch 'aw/validate-proxy-url-scheme'
Junio C Hamano [Thu, 21 May 2026 03:06:48 +0000 (12:06 +0900)] 
Merge branch 'aw/validate-proxy-url-scheme'

Misspelt proxy URL (e.g., httt://...) did not trigger any warning
or failure, which has been corrected.

* aw/validate-proxy-url-scheme:
  http: reject unsupported proxy URL schemes

3 weeks agoMerge branch 'mm/git-url-parse'
Junio C Hamano [Thu, 21 May 2026 03:06:48 +0000 (12:06 +0900)] 
Merge branch 'mm/git-url-parse'

The internal URL parsing logic has been made accessible via a new
subcommand "git url-parse".

* mm/git-url-parse:
  t9904: add tests for the new url-parse builtin
  doc: describe the url-parse builtin
  builtin: create url-parse command
  urlmatch: define url_parse function
  url: return URL_SCHEME_UNKNOWN instead of dying
  url: move scheme detection to URL header/source
  url: move url_is_local_not_ssh to url.h
  connect: rename enum protocol to url_scheme

3 weeks agoMerge branch 'pw/xdiff-shrink-memory-consumption'
Junio C Hamano [Thu, 21 May 2026 03:06:48 +0000 (12:06 +0900)] 
Merge branch 'pw/xdiff-shrink-memory-consumption'

Shrink wasted memory in Myers diff that does not account for common
prefix and suffix removal.

* pw/xdiff-shrink-memory-consumption:
  xdiff: reduce the size of array
  xprepare: simplify error handling
  xdiff: cleanup xdl_clean_mmatch()
  xdiff: reduce size of action arrays

3 weeks agoMerge branch 'kh/doc-log-decorate-list'
Junio C Hamano [Thu, 21 May 2026 03:06:47 +0000 (12:06 +0900)] 
Merge branch 'kh/doc-log-decorate-list'

Doc update.

* kh/doc-log-decorate-list:
  doc: log: use the same delimiter in description list
  doc: log: fix --decorate description list

3 weeks agoMerge branch 'kn/refs-generic-helpers'
Junio C Hamano [Thu, 21 May 2026 03:06:47 +0000 (12:06 +0900)] 
Merge branch 'kn/refs-generic-helpers'

Refactor service routines in the ref subsystem backends.

* kn/refs-generic-helpers:
  refs: use peeled tag values in reference backends
  refs: add peeled object ID to the `ref_update` struct
  refs: move object parsing to the generic layer
  update-ref: handle rejections while adding updates
  update-ref: move `print_rejected_refs()` up
  refs: return `ref_transaction_error` from `ref_transaction_update()`
  refs: extract out reflog config to generic layer
  refs: introduce `ref_store_init_options`
  refs: remove unused typedef 'ref_transaction_commit_fn'

3 weeks agoMerge branch 'za/t2000-modernise-more'
Junio C Hamano [Thu, 21 May 2026 03:06:47 +0000 (12:06 +0900)] 
Merge branch 'za/t2000-modernise-more'

Test update.

* za/t2000-modernise-more:
  t2000: consolidate second scenario into a single test block

3 weeks agocommit: fall back to full read when maybe_tree is NULL
Jeff King [Tue, 19 May 2026 06:15:34 +0000 (02:15 -0400)] 
commit: fall back to full read when maybe_tree is NULL

When we load a commit object from the commit graph (rather than reading
the object contents), we don't fill in its "maybe_tree" entry, but
rather wait to lazy-load it. This goes back to 7b8a21dba1 (commit-graph:
lazy-load trees for commits, 2018-04-06), and saves the work of
instantiating tree objects that nobody cares about.

But it creates a data dependency: now the commit struct depends on the
graph file to do that lazy load. This is a problem if we close the graph
file; now we have a commit struct that claims to be parsed but is
missing some of its data.

It's rare for this to be a problem in practice, because we don't tend to
close the graph files at all, and if we do we don't tend to look at
their commits afterward. But there is one case that is easy to trigger:
git-clone's --dissociate option will close the object database before
running the dissociate repack, and then afterwards still try to check
out the working tree. This will yield an error like:

  fatal: unable to parse commit b29edc0babef41810f7b1c9ee1d74058f22e4080
  warning: Clone succeeded, but checkout failed.

What happens is that we expect repo_get_commit_tree() to lazy-load the
tree, but commit_graph_position() returns COMMIT_NOT_FROM_GRAPH because
the position slab has gone away (and even if it hadn't, we don't have
the graph file itself available anymore).

Let's try harder to find the tree in repo_get_commit_tree() by actually
opening the commit object and parsing the tree line. This is extra work,
but no more than we'd have to go to if we hadn't done the initial graph
load in the first place.

It does mean that a corrupt commit (e.g., one that points to a non-tree
object for which we couldn't instantiate a struct) will repeatedly load
the object from disk, once for each call to repo_get_commit_tree(). But
such corruptions should be rare, and we don't tend to perform such calls
repeatedly (usually we'd abort the operation upon seeing corruption).

It also means we have to reimplement a bit of the commit parsing. We
can't just use parse_commit_buffer() here, because it expects an
unparsed struct and wants to load everything, including parent links.
But we don't know if the parent list has been munged during traversal,
so it's not safe for us to touch it. Fortunately, it's quite easy to
load just the tree, as it is always the first line of the commit object.

There is an alternative approach which I considered but rejected:
"complete" each graph-loaded commit struct when we close the graph file
by looking up and instantiating their trees at close time. This is the
most elegant solution in some sense, as it resolves the data dependency
at the moment it goes away. And it avoids ever opening the commit
objects at all, which can be more efficient.

But not always. The resolving effort scales with the number of
graph-loaded commits, even though we may only later access one or a few.
So the tradeoff depends on how many were loaded in total versus how many
will be later accessed.

And in most cases, we will not access any at all! Programs which close
the object database before exiting will then do a bunch of work for no
reason. This could be mitigated by requiring a separate function to
resolve the graph structs before closing the file. But now each close
call has to consider whether to call that resolving function. So we'd
fix this case in git-clone, but we don't know what other cases (if any)
are lurking.

Moreover, this strategy does nothing if we lose access to the graph file
unexpectedly (e.g., due to a system error). I'm not entirely sure this
is possible now (we mmap it, so I'd guess any error would turn into
SIGBUS anyway). But it feels like making the lazy-load more robust
(which this patch does) is the best way to handle a wide variety of
possible failure modes.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosend-pack: pass negotiation config in push
Derrick Stolee [Tue, 19 May 2026 16:24:55 +0000 (16:24 +0000)] 
send-pack: pass negotiation config in push

When push.negotiate is enabled, 'git push' spawns a child 'git fetch
--negotiate-only' process to find common commits.  Pass
--negotiation-include and --negotiation-restrict options from the
'remote.<name>.negotiationInclude' and
'remote.<name>.negotiationRestrict' config keys to this child process.

When negotiationRestrict is configured, it replaces the default
behavior of using all remote refs as negotiation tips. This allows
the user to control which local refs are used for push negotiation.

When negotiationInclude is configured, the specified ref patterns
are passed as --negotiation-include to ensure their tips are always
sent as 'have' lines during push negotiation.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agoremote: add remote.*.negotiationInclude config
Derrick Stolee [Tue, 19 May 2026 16:24:54 +0000 (16:24 +0000)] 
remote: add remote.*.negotiationInclude config

Add a new 'remote.<name>.negotiationInclude' multi-valued config option that
provides default values for --negotiation-include when no
--negotiation-include arguments are specified over the command line.  This
is a mirror of how 'remote.<name>.negotiationRestrict' specifies defaults
for the --negotiation-restrict arguments.

Each value is either an exact ref name or a glob pattern whose tips should
always be sent as 'have' lines during negotiation. The config values are
resolved through the same resolve_negotiation_include() codepath as the CLI
options.

This option is additive with the normal negotiation process: the negotiation
algorithm still runs and advertises its own selected commits, but the refs
matching the config are sent unconditionally on top of those heuristically
selected commits.

Similar to the negotiationRestrict config, an empty value resets the value
list to allow ignoring earlier config values, such as those that might be
set in system or global config.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agofetch: add --negotiation-include option for negotiation
Derrick Stolee [Tue, 19 May 2026 16:24:53 +0000 (16:24 +0000)] 
fetch: add --negotiation-include option for negotiation

Add a new --negotiation-include option to 'git fetch', which ensures
that certain ref tips are always sent as 'have' lines during fetch
negotiation, regardless of what the negotiation algorithm selects.

This is useful when the repository has a large number of references, so
the normal negotiation algorithm truncates the list. This is especially
important in repositories with long parallel commit histories. For
example, a repo could have a 'dev' branch for development and a
'release' branch for released versions. If the 'dev' branch isn't
selected for negotiation, then it's not a big deal because there are
many in-progress development branches with a shared history. However, if
'release' is not selected for negotiation, then the server may think
that this is the first time the client has asked for that reference,
causing a full download of its parallel commit history (and any extra
data that may be unique to that branch). This is based on a real example
where certain fetches would grow to 60+ GB when a release branch
updated.

This option is a complement to --negotiation-restrict, which reduces the
negotiation ref set to a specific list. In the earlier example, using
--negotiation-restrict to focus the negotiation to 'dev' and 'release'
would avoid those problematic downloads, but would still not allow
advertising potentially-relevant user branches. In this way, the
'include' version solves the problem I mention while allowing
negotiation to pick other references opportunistically. The two options
can also be combined to allow the best of both worlds.

The argument may be an exact ref name or a glob pattern. Non-existent
refs are silently ignored. This behavior is also updated in the ref matching
logic for the related --negotiation-restrict option to match.

The implementation outputs the requested objects as haves before the
negotiator performs its own algorithm to choose the next haves. Use the new
have_sent() interface to signal these have commits were sent before engaging
with the negotiator's next() iterator.

Also add --negotiation-include to 'git pull' passthrough options.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agonegotiator: add have_sent() interface
Derrick Stolee [Tue, 19 May 2026 16:24:52 +0000 (16:24 +0000)] 
negotiator: add have_sent() interface

In a future change, we will introduce a capability to choose specific commit
OIDs as 'have's in fetch negotiation, with the ability to have the
negotiator choose more 'have's to increase coverage beyond that required
core set. The negotiator works to avoid emitting 'have's that can reach each
other, but that logic is hidden beneath the negotiator's iterator function
pointer ('next'). We need a way to communicate to the negotiator that we
have picked a 'have' so it could incorporate that into its logic.

Add a have_sent() method to the fetch_negotiator interface. This is the
signal that allows the negotiator to track the commit as already shown and
can perform the proper bookkeeping to avoid emitting those objects or
anything they can reach.

For our non-trivial negotiators, it is sufficient to mark these commits as
common, so the implementation is quite simple. This logic will be exercised
in the next change.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agoremote: add remote.*.negotiationRestrict config
Derrick Stolee [Tue, 19 May 2026 16:24:51 +0000 (16:24 +0000)] 
remote: add remote.*.negotiationRestrict config

In a previous change, the --negotiation-restrict command-line option of 'git
fetch' was added as a synonym of --negotiation-tip. Both of these options
restrict the set of 'haves' the client can send as part of negotiation.

This was previously not available via a configuration option. Add a new
'remote.<name>.negotiationRestrict' multi-valued config option that updates
'git fetch <name>' to use these restrictions by default.

If the user provides even one --negotiation-restrict argument, then the
config is ignored.

An empty value resets the value list to allow ignoring earlier config
values, such as those that might be set in system or global config.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agotransport: rename negotiation_tips
Derrick Stolee [Tue, 19 May 2026 16:24:50 +0000 (16:24 +0000)] 
transport: rename negotiation_tips

The previous change added the --negotiation-restrict synonym for the
--negotiation-tip option for 'git fetch'. In anticipation of adding a new
option that behaves similarly but with distinct changes to its behavior,
rename the internal representation of this data from 'negotiation_tips' to
'negotiation_restrict_tips'.

The 'tips' part is kept because this is an oid_array in the transport layer.
This requires the builtin to handle parsing refs into collections of oids so
the transport layer can handle this cleaner form of the data.

Also update the string_list used to store the inputs from command-line
options.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agofetch: add --negotiation-restrict option
Derrick Stolee [Tue, 19 May 2026 16:24:49 +0000 (16:24 +0000)] 
fetch: add --negotiation-restrict option

The --negotiation-tip option to 'git fetch' and 'git pull' allows users
to specify that they want to focus negotiation on a small set of
references. This is a _restriction_ on the negotiation set, helping to
focus the negotiation when the ref count is high. However, it doesn't
allow for the ability to opportunistically select references beyond that
list.

This subtle detail that this is a 'maximum set' and not a 'minimum set'
is not immediately clear from the option name. This makes it more
complicated to add a new option that provides the complementary behavior
of a minimum set.

For now, create a new synonym option, --negotiation-restrict, that
behaves identically to --negotiation-tip. Update the documentation to
make it clear that this new name is the preferred option, but we keep
the old name for compatibility. Mark --negotiation-tip as an alias of the
new, preferred option.

Update a few warning messages with the new option, but also make them
translatable with the option name inserted by formatting. At least one
of these messages will be reused later for a new option.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agot5516: fix test order flakiness
Derrick Stolee [Tue, 19 May 2026 16:24:48 +0000 (16:24 +0000)] 
t5516: fix test order flakiness

The 'fetch follows tags by default' test sorts using 'sort -k 4', but
for-each-ref output only has 3 columns. This relies on sort treating records
with fewer fields as having an empty fourth field, which may produce
unstable results depending on locale. This appears to be an accident added
in 3f763ddf28 (fetch: set remote/HEAD if it does not exist, 2024-11-22).

Use 'sort -k 3' to match the actual number of columns in the output.

Reviewed-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Derrick Stolee <stolee@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agoMerge branch 'tb/pseudo-merge-bugfixes' into tb/bitmap-build-performance
Junio C Hamano [Wed, 20 May 2026 02:32:39 +0000 (11:32 +0900)] 
Merge branch 'tb/pseudo-merge-bugfixes' into tb/bitmap-build-performance

* tb/pseudo-merge-bugfixes:
  pack-bitmap: prevent pattern leak on pseudo-merge re-assignment
  Documentation: fix broken `sampleRate` in gitpacking(7)
  pack-bitmap: reject pseudo-merge "sampleRate" of 0
  pack-bitmap: parse commits in `find_pseudo_merge_group_for_ref()`
  pack-bitmap: fix pseudo-merge lookup for shared commits
  pack-bitmap: fix inverted binary search in `pseudo_merge_at()`
  pack-bitmap-write: sort pseudo-merge commit lookup table in pack order
  t5333: demonstrate various pseudo-merge bugs
  t/helper: add 'test-tool bitmap write' subcommand

4 weeks agorepack: allow `--write-midx=incremental` without `--geometric`
Taylor Blau [Tue, 19 May 2026 15:58:25 +0000 (11:58 -0400)] 
repack: allow `--write-midx=incremental` without `--geometric`

Previously, `--write-midx=incremental` required `--geometric` and would
die() without it. Relax this restriction so that incremental MIDX
repacking can be used independently.

Without `--geometric`, the behavior is append-only: a single new MIDX
layer is created containing whatever packs were written by the repack
and appended to the existing chain (or a new chain is started). Existing
layers are preserved as-is with no compaction or merging.

Implement this via a new repack_make_midx_append_plan() that builds a
plan consisting of a WRITE step for the freshly written packs followed
by COPY steps for every existing MIDX layer. The existing compaction
plan (repack_make_midx_compaction_plan) is used only when `--geometric`
is active.

Update the documentation to describe the behavior with and without
`--geometric`, and replace the test that enforced the old restriction
with one exercising append-only incremental MIDX repacking.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agorepack: introduce `--write-midx=incremental`
Taylor Blau [Tue, 19 May 2026 15:58:22 +0000 (11:58 -0400)] 
repack: introduce `--write-midx=incremental`

Expose the incremental MIDX repacking mode (implemented in an earlier
commit) via a new --write-midx=incremental option for `git repack`.

Add "incremental" as a recognized argument to the --write-midx
OPT_CALLBACK, mapping it to REPACK_WRITE_MIDX_INCREMENTAL. When this
mode is active and --geometric is in use, set the midx_layer_threshold
on the pack geometry so that only packs in sufficiently large tip layers
are considered for repacking.

Two new configuration options control the compaction behavior:

 - repack.midxSplitFactor (default: 2): the factor used in the
   geometric merging condition for MIDX layers.

 - repack.midxNewLayerThreshold (default: 8): the minimum number of
   packs in the tip MIDX layer before its packs are considered as
   candidates for geometric repacking.

Add tests exercising the new mode across a variety of scenarios
including basic geometric violations, multi-round chain integrity,
branching and merging histories, cross-layer object uniqueness, and
threshold-based compaction.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agorepack: implement incremental MIDX repacking
Taylor Blau [Tue, 19 May 2026 15:58:19 +0000 (11:58 -0400)] 
repack: implement incremental MIDX repacking

Implement the `write_midx_incremental()` function, which builds and
maintains an incremental MIDX chain as part of the geometric repacking
process.

Unlike the default mode which writes a single flat MIDX, the incremental
mode constructs a compaction plan that determines which MIDX layers to
write, compact, or copy, and then executes each step using `git
multi-pack-index` subcommands with the --no-write-chain-file flag.

The repacking strategy works as follows:

 * Acquire the lock guarding the multi-pack-index-chain.

 * A new MIDX layer is always written containing the newly created
   pack(s). If the tip MIDX layer was rewritten during geometric
   repacking, any surviving packs from that layer are also included.

 * Starting from the new layer, adjacent MIDX layers are merged together
   as long as the accumulated object count exceeds half the object count
   of the next deeper layer (controlled by 'repack.midxSplitFactor').

 * Remaining layers in the chain are evaluated pairwise and either
   compacted or copied as-is, following the same merging condition.

 * Write the contents of the new multi-pack-index chain, atomically move
   it into place, and then release the lock.

 * Delete any now-unused MIDX layers.

After writing the new layer, the strategy is evaluated among the
existing MIDX layers in order from oldest to newest. Each step that
writes a new MIDX layer uses "--no-write-chain-file" to avoid updating
the multi-pack-index-chain file. After all steps are complete, the new
chain file is written and then atomically moved into place.

At present, this functionality is exposed behind a new enum value,
`REPACK_WRITE_MIDX_INCREMENTAL`, but has no external callers. A
subsequent commit will expose this mode via `git repack
--write-midx=incremental`.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agopackfile: ensure `close_pack_revindex()` frees in-memory revindex
Taylor Blau [Tue, 19 May 2026 15:58:16 +0000 (11:58 -0400)] 
packfile: ensure `close_pack_revindex()` frees in-memory revindex

The following commit will introduce a case where we write a MIDX bitmap
over packs that do not themselves have on-disk *.rev files.

This case is supported within Git, and we will simply fall back to
generating the revindex in memory. But we don't ever release that
memory, causing a leak that is exposed by a test introduced in the
following commit.

(As far as I could find, we never free()'d memory allocated as a
byproduct of creating an in-memory revindex, likely because that code
predates the leak-checking niceties we have in the test suite now.)

Rectify this by calling `FREE_AND_NULL()` on the `p->revindex` field
when calling `close_pack_revindex()`.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agobuiltin/repack.c: convert `--write-midx` to an `OPT_CALLBACK`
Taylor Blau [Tue, 19 May 2026 15:58:13 +0000 (11:58 -0400)] 
builtin/repack.c: convert `--write-midx` to an `OPT_CALLBACK`

Change the --write-midx (-m) flag from an OPT_BOOL to an OPT_CALLBACK
that accepts an optional mode argument. Introduce an enum with
REPACK_WRITE_MIDX_NONE and REPACK_WRITE_MIDX_DEFAULT to distinguish
between the two states, and update all existing boolean checks
accordingly.

For now, passing no argument (or just `-m`) selects the default mode,
preserving existing behavior. A subsequent commit will add a new mode
for writing incremental MIDXs.

Extract repack_write_midx() as a dispatcher that selects the
appropriate MIDX-writing implementation based on the mode.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agorepack-geometry: prepare for incremental MIDX repacking
Taylor Blau [Tue, 19 May 2026 15:58:10 +0000 (11:58 -0400)] 
repack-geometry: prepare for incremental MIDX repacking

Teach `pack_geometry_init()` to optionally restrict the set of
repacking candidates to only packs in the tip MIDX layer when a
`midx_layer_threshold` is configured. If the tip layer has fewer packs
than the threshold, those packs are excluded entirely; otherwise only
packs in that layer participate in the geometric repack.

Also track whether any tip-layer packs were included in the rollup
(`midx_tip_rewritten`), which a subsequent commit will use to decide
how to update the MIDX chain after repacking.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agorepack-midx: extract `repack_fill_midx_stdin_packs()`
Taylor Blau [Tue, 19 May 2026 15:58:07 +0000 (11:58 -0400)] 
repack-midx: extract `repack_fill_midx_stdin_packs()`

The function `write_midx_included_packs()` manages the lifecycle of
writing packs to stdin when running `git multi-pack-index write` as a
child process.

Extract a standalone `repack_fill_midx_stdin_packs()` helper, which
handles `--stdin-packs` argument setup, starting the command, writing
pack names to its standard input, and finishing the command.

This simplifies `write_midx_included_packs()` and prepares for a
subsequent commit where the same helper is called with `cmd->out = -1`
to capture the MIDX's checksum from the command's standard output,
which is needed when writing MIDX layers with `--no-write-chain-file`.

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>
4 weeks agorepack-midx: factor out `repack_prepare_midx_command()`
Taylor Blau [Tue, 19 May 2026 15:58:03 +0000 (11:58 -0400)] 
repack-midx: factor out `repack_prepare_midx_command()`

The `write_midx_included_packs()` function assembles and executes a
`git multi-pack-index write` command, constructing the argument list
inline.

Future commits will introduce additional callers that need to construct
similar `git multi-pack-index` commands (for both `write` and `compact`
subcommands), so extract the common portions of the command setup into a
reusable `repack_prepare_midx_command()` helper.

The extracted helper sets `git_cmd`, pushes `multi-pack-index` and a
subcommand, and handles `--progress`/`--no-progress` and `--bitmap`
flags. The remaining arguments that are specific to the `write`
subcommand (such as `--stdin-packs`) are left to the caller.

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>
4 weeks agomidx: expose `midx_layer_contains_pack()`
Taylor Blau [Tue, 19 May 2026 15:58:00 +0000 (11:58 -0400)] 
midx: expose `midx_layer_contains_pack()`

Rename the function `midx_contains_pack_1()` to instead be called
`midx_layer_contains_pack()` and make it accessible. Unlike
`midx_contains_pack()` (which recurses through the entire chain), this
function checks only a single MIDX layer.

This will be used by a subsequent commit to determine whether a given
pack belongs to the tip MIDX layer specifically, rather than to any
layer in the chain.

No functional changes are present in this commit.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agorepack: track the ODB source via existing_packs
Taylor Blau [Tue, 19 May 2026 15:57:57 +0000 (11:57 -0400)] 
repack: track the ODB source via existing_packs

Store the ODB source in the `existing_packs` struct and use that in
place of the raw `repo->objects->sources` access within `cmd_repack()`.

The source used is still assigned from the first source in the list, so
there are no functional changes in this commit. The changes instead
serve two purposes (one immediate, one not):

 - The incremental MIDX-based repacking machinery will need to know what
   source is being used to read the existing MIDX/chain (should one
   exist).

 - In the future, if "git repack" is taught how to operate on other
   object sources, this field will serve as the authoritative value for
   that source.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agomidx: support custom `--base` for incremental MIDX writes
Taylor Blau [Tue, 19 May 2026 15:57:54 +0000 (11:57 -0400)] 
midx: support custom `--base` for incremental MIDX writes

Both `compact` and `write --incremental` fix the base of the resulting
MIDX layer: `compact` always places the compacted result on top of
"from's" immediate parent in the chain, and `write --incremental` always
appends a new layer to the existing tip. In both cases the base is not
configurable.

Future callers need additional flexibility. For instance, the incremental
MIDX-based repacking code may wish to write a layer based on some
intermediate ancestor rather than the current tip, or produce a root
layer when replacing the bottommost entries in the chain.

Introduce a new `--base` option for both subcommands to specify the
checksum of the MIDX layer to use as the base. The given checksum must
refer to a valid layer in the MIDX chain that is an ancestor of the
topmost layer being written or compacted.

The special value "none" is accepted to produce a root layer with no
parent. This will be needed when the incremental repacking machinery
determines that the bottommost layers of the chain should be replaced.

If no `--base` is given, behavior is unchanged: `compact` uses "from's"
immediate parent in the chain, and `write` appends to the existing tip.

For the `write` subcommand, `--base` requires `--no-write-chain-file`. A plain
`write --incremental` appends a new layer to the live chain tip with no
mechanism to atomically replace it; overriding the base would produce a
layer that does not extend the tip, breaking chain invariants. With
`--no-write-chain-file` the chain is left unmodified and the caller is
responsible for assembling a valid chain.

For `compact`, no such restriction applies. The compaction operation
atomically replaces the compacted range in the chain file, so writing
the result on top of any valid ancestor preserves chain invariants.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agomidx: introduce `--no-write-chain-file` for incremental MIDX writes
Taylor Blau [Tue, 19 May 2026 15:57:51 +0000 (11:57 -0400)] 
midx: introduce `--no-write-chain-file` for incremental MIDX writes

When writing an incremental MIDX layer, the MIDX machinery writes the
new layer into the multi-pack-index.d directory and then updates the
multi-pack-index-chain file to include the freshly written layer.

Future callers however may not wish to immediately update the MIDX chain
itself, preferring instead to write out new layer(s) themselves before
atomically updating the chain. Concretely, the new incremental
MIDX-based repacking strategy will want to do exactly this (that is,
assemble the new MIDX chain itself before writing a new chain file and
atomically linking it into place).

Introduce a `--no-write-chain-file` flag that:

 * writes the new MIDX layer into the multi-pack-index.d directory

 * prints its checksum

 * does not update the multi-pack-index-chain file.

The MIDX chain file (and thus, the lock protecting it) remain untouched,
allowing callers to assemble the chain themselves. This flag requires
`--incremental`, since the notion of a separate layer only makes sense
for incremental MIDXs.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agomidx: use `strvec` for `keep_hashes`
Taylor Blau [Tue, 19 May 2026 15:57:48 +0000 (11:57 -0400)] 
midx: use `strvec` for `keep_hashes`

The `keep_hashes` array in `write_midx_internal()` accumulates the
checksums of MIDX files that should be retained when pruning stale
entries from the MIDX chain. For similar reasons as in a previous
commit, rewrite this using a strvec, requiring us to pass one fewer
parameter.

Unlike the aforementioned previous commit, use a `strvec` instead of a
`string_list`, which provides a more ergonomic interface to adjust the
values at a particular index. The ordering is important here, as this
value is used to determine the contents of the resulting
`multi-pack-index-chain` file when writing with "--incremental".

Since the previous commit already builds the array in forward order, the
conversion is straightforward: replace indexed assignments with
`strvec_push()`, drop the pre-counting and `CALLOC_ARRAY()`, and
simplify cleanup via `strvec_clear()`.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agomidx: build `keep_hashes` array in order
Taylor Blau [Tue, 19 May 2026 15:57:45 +0000 (11:57 -0400)] 
midx: build `keep_hashes` array in order

Instead of filling the keep_hashes array using reverse indexing (e.g.,
`keep_hashes[count - i - 1]`) while traversing linked lists forward,
collect linked list nodes into a temporary `layers` array and then
iterate it backwards to fill `keep_hashes` sequentially.

This makes the filling logic easier to follow, since each segment of the
array is filled with a simple forward-marching index. Moreover, this
change prepares us for a subsequent commit that will switch to using a
`strvec`.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agomidx: use `strset` for retained MIDX files
Taylor Blau [Tue, 19 May 2026 15:57:42 +0000 (11:57 -0400)] 
midx: use `strset` for retained MIDX files

Both `clear_midx_files_ext()` and `clear_incremental_midx_files_ext()`
build a list of filenames to keep while pruning stale MIDX files. Today
they hand-roll an array instead of using a `strset`, thus requiring us
to pass an additional length parameter, and makes lookups linear.

Replace the bare array with a `strset` which can be passed around as a
single parameter. Though it improves lookup performance, the difference
is likely immeasurable given how small the keep_hashes array typically
is.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agomidx-write: handle noop writes when converting incremental chains
Taylor Blau [Tue, 19 May 2026 15:57:39 +0000 (11:57 -0400)] 
midx-write: handle noop writes when converting incremental chains

When updating a MIDX, we optimize out writes that will result in an
identical MIDX as the one we already have on disk. See b3bab9d2729
(midx-write: extract function to test whether MIDX needs updating,
2025-12-10) for more details on exactly which writes are optimized out.

If `midx_needs_update()` can't rule out any of the obvious cases (e.g.,
the checksum is invalid, we're requesting a different version, or
performing compaction which always requires an update), then we compare
the packs we're writing to the packs we already know about. If there are
an equal number of packs being written as there are in any existing
MIDX layer(s), then we compare the packs by their name.

This comparison fails when we have an incremental MIDX chain with
at least two layers, since we do not recursively peel through earlier
layers, instead treating the `->pack_names` array of the tip MIDX layer
as containing all `m->num_packs + m->num_packs_in_base` packs.

Adjust this to instead look through the MIDX layers one by one when
comparing pack names. While we're at it, fix a typo above in the same
function.

Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agoThe 5th batch
Junio C Hamano [Wed, 20 May 2026 01:30:45 +0000 (10:30 +0900)] 
The 5th batch

Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agoMerge branch 'sb/unpack-index-pack-buffer-resize'
Junio C Hamano [Wed, 20 May 2026 01:30:58 +0000 (10:30 +0900)] 
Merge branch 'sb/unpack-index-pack-buffer-resize'

Use a larger buffer size in the code paths to ingest pack stream.

* sb/unpack-index-pack-buffer-resize:
  index-pack, unpack-objects: increase input buffer from 4 KiB to 128 KiB

4 weeks agoMerge branch 'ps/history-fixup'
Junio C Hamano [Wed, 20 May 2026 01:30:57 +0000 (10:30 +0900)] 
Merge branch 'ps/history-fixup'

"git history" learned "fixup" command.

* ps/history-fixup:
  builtin/history: introduce "fixup" subcommand
  builtin/history: generalize function to commit trees
  replay: allow callers to control what happens with empty commits

4 weeks agoMerge branch 'jh/alias-i18n-fixes'
Junio C Hamano [Wed, 20 May 2026 01:30:57 +0000 (10:30 +0900)] 
Merge branch 'jh/alias-i18n-fixes'

Further update to the i18n alias support to avoid regressions.

* jh/alias-i18n-fixes:
  alias: restore support for simple dotted aliases

4 weeks agoMerge branch 'bc/sign-commit-with-custom-encoding'
Junio C Hamano [Wed, 20 May 2026 01:30:57 +0000 (10:30 +0900)] 
Merge branch 'bc/sign-commit-with-custom-encoding'

Signing commit with custom encoding was passing the data to be
signed at a wrong stage in the pipeline, which has been corrected.

* bc/sign-commit-with-custom-encoding:
  commit: sign commit after mutating buffer
  commit: name UTF-8 function appropriately

4 weeks agoMerge branch 'js/adjust-tests-to-explicitly-access-bare-repo'
Junio C Hamano [Wed, 20 May 2026 01:30:56 +0000 (10:30 +0900)] 
Merge branch 'js/adjust-tests-to-explicitly-access-bare-repo'

Some tests assume that bare repository accesses are by default
allowed; rewrite some of them to avoid the assumption, rewrite
others to explicitly set safe.bareRepository to allow them.

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

4 weeks agoMerge branch 'en/diffstat-utf8-truncation-fix'
Junio C Hamano [Wed, 20 May 2026 01:30:56 +0000 (10:30 +0900)] 
Merge branch 'en/diffstat-utf8-truncation-fix'

The computation to shorten the filenames shown in diffstat measured
width of individual UTF-8 characters to add up, but forgot to take
into account error cases (e.g., an invalid UTF-8 sequence, or a
control character).

* en/diffstat-utf8-truncation-fix:
  diff: fix out-of-bounds reads and NULL deref in diffstat UTF-8 truncation

4 weeks agoMerge branch 'js/mingw-no-nedmalloc'
Junio C Hamano [Wed, 20 May 2026 01:30:56 +0000 (10:30 +0900)] 
Merge branch 'js/mingw-no-nedmalloc'

Stop using unmaintained custom allocator in Windows build which was
the last user of the code.

* js/mingw-no-nedmalloc:
  mingw: remove the vendored compat/nedmalloc/ subtree
  mingw: drop the build-system plumbing for nedmalloc
  mingw: stop using nedmalloc

4 weeks agoMerge branch 'js/objects-larger-than-4gb-on-windows'
Junio C Hamano [Wed, 20 May 2026 01:30:56 +0000 (10:30 +0900)] 
Merge branch 'js/objects-larger-than-4gb-on-windows'

Update code paths that assumed "unsigned long" was long enough for
"size_t".

* js/objects-larger-than-4gb-on-windows:
  ci: run expensive tests on push builds to integration branches
  t5608: mark >4GB tests as EXPENSIVE
  test-tool synthesize: add precomputed SHA-256 pack for 4 GiB + 1
  test-tool synthesize: precompute pack for 4 GiB + 1
  test-tool synthesize: use the unsafe hash for speed
  t5608: add regression test for >4GB object clone
  test-tool: add a helper to synthesize large packfiles
  delta, packfile: use size_t for delta header sizes
  odb, packfile: use size_t for streaming object sizes
  git-zlib: handle data streams larger than 4GB
  index-pack, unpack-objects: use size_t for object size

4 weeks agosetup: stop using `the_repository` in `init_db()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:22 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `init_db()`

Stop using `the_repository` in `init_db()` and instead accept
the repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `create_reference_database()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:21 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `create_reference_database()`

Stop using `the_repository` in `create_reference_database()` and instead
accept the repository as a parameter. The injection of `the_repository`
is thus bumped one level higher, where callers now pass it in
explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `initialize_repository_version()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:20 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `initialize_repository_version()`

Stop using `the_repository` in `initialize_repository_version()` and
instead accept the repository as a parameter. The injection of
`the_repository` is thus bumped one level higher, where callers now pass
it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `check_repository_format()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:19 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `check_repository_format()`

Stop using `the_repository` in `check_repository_format()` and instead
accept the repository as a parameter. The injection of `the_repository`
is thus bumped one level higher, where callers now pass it in
explicitly.

Furthermore, the function is never used outside "setup.c". Drop its
declaration in "setup.h" and make it static. Note that this requires us
to reorder the function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `upgrade_repository_format()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:18 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `upgrade_repository_format()`

Stop using `the_repository` in `upgrade_repository_format()` and instead
accept the repository as a parameter. The injection of `the_repository`
is thus bumped one level higher, where callers now pass it in
explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `setup_git_directory()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:17 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `setup_git_directory()`

Stop using `the_repository` in `setup_git_directory()` and instead
accept the repository as a parameter. The injection of `the_repository`
is thus bumped one level higher, where callers now pass it in
explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `setup_git_directory_gently()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:16 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `setup_git_directory_gently()`

Stop using `the_repository` in `setup_git_directory_gently()` and
instead accept the repository as a parameter. The injection of
`the_repository` is thus bumped one level higher, where callers now pass
it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `setup_git_env()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:15 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `setup_git_env()`

Stop using `the_repository` in `setup_git_env()` and instead accept the
repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Furthermore, the function is never used outside of "setup.c". Drop the
declaration in "environment.h" and make it static.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `set_git_work_tree()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:14 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `set_git_work_tree()`

Stop using `the_repository` in `set_git_work_tree()` and instead accept
the repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Similar as with the preceding commit, we track whether the worktree has
been initialized already via a global variable so that we can die in
case the repository is re-initialized with a different worktree path.
Store this info in the `struct repository` instead so that we correctly
handle this per repository.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `setup_work_tree()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:13 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `setup_work_tree()`

Stop using `the_repository` in `setup_work_tree()` and instead accept
the repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Note that the function tracks two bits of information via global
variables. This of course doesn't make much sense anymore now that we
can set up worktrees for arbitrary repositories:

  - We track whether the worktree has already been initialized and, if
    so, we skip the call to `chdir_notify()` and setenv(3p). It does not
    make much sense to store this info in the repository, as we _would_
    want to update the environment when switching between worktrees back
    and forth.

    So instead of storing this info in the repository, we drop this
    state entirely and live with the fact that we may execute the logic
    twice. It should ultimately be idempotent though and thus not be
    much of a problem.

  - We track whether the worktree configuration is bogus. If so, and if
    later on some caller tries to setup the worktree, then we'll die
    instead. This is indeed information that we can move into the
    repository itself.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `enter_repo()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:12 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `enter_repo()`

Stop using `the_repository` in `enter_repo()` and instead accept the
repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `verify_non_filename()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:11 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `verify_non_filename()`

Stop using `the_repository` in `verify_non_filename()` and instead
accept the repository as a parameter. The injection of `the_repository`
is thus bumped one level higher, where callers now pass it in
explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `verify_filename()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:10 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `verify_filename()`

Stop using `the_repository` in `verify_filename()` and instead accept
the repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `path_inside_repo()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:09 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `path_inside_repo()`

Stop using `the_repository` in `path_inside_repo()` and instead accept
the repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `prefix_path()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:08 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `prefix_path()`

Stop using `the_repository` in `prefix_path()` and instead accept the
repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
4 weeks agosetup: stop using `the_repository` in `is_inside_work_tree()`
Patrick Steinhardt [Tue, 19 May 2026 09:52:07 +0000 (11:52 +0200)] 
setup: stop using `the_repository` in `is_inside_work_tree()`

Similar as with the preceding commit, `is_inside_work_tree()` determines
whether the current working directory is located inside the worktree of
`the_repository`. Perform the same refactoring by dropping the caching
mechanism and injecting the repository that shall be checked.

Note that, same as in the preceding commit, we're also resolving the
worktree path via `realpath()`. In theory this step is not necessary as
we always set the worktree path via `repo_set_worktree()`, and that
function already resolves the path for us. But resolving the path a
second time is unlikely to matter performance-wise, and it feels fragile
to rely on the repository's worktree path being absolute. We thus
perform the same extra step even though it's ultimately not required.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>