summaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2026-05-28pack-bitmap: build pseudo-merge bitmaps after regular bitmapsTaylor Blau1-36/+174
When generating bitmaps, `bitmap_builder_init()` starts with an initial selection of commits to receive bitmap coverage, and then determines a set of "maximal" commits based on its input. Commit 089f751360f (pack-bitmap-write: build fewer intermediate bitmaps, 2020-12-08) has extensive details, but the gist is as follows: Each selected commit starts with one commit_mask bit in its "commit mask" bitmap. Then, we walk the first-parent history in topological order and OR each commit's mask into its (first) parent. Whenever that OR results in the parent having more bits set, the child is deemed to be non-maximal, and the frontier is pushed further back along the first parent history. That approach works extremely well for ordinary selected commits, whose first-parent histories often describe real sharing between the bitmaps we are going to write. It struggles, however, to efficiently generate pseudo-merge bitmaps. Unlike ordinary commits for which the above algorithm is designed, pseudo-merges don't represent any "real" commit in history, just a grouping of non-bitmapped reference tips. In that sense, their first parent is just a part of a larger set, and treating them like ordinary selected commits imposes a significant slow-down when generating bitmaps with pseudo-merges enabled. Consider partitioning all non-bitmapped reference tips into eight individual pseudo-merges via the following configuration: [bitmapPseudoMerge "all"] pattern=refs/ threshold=now stableSize=10000000 maxMerges=8 , the cost of generating a bitmap from scratch rises significantly: +------------------+-----------------+---------------+---------------------+ | | no pseudo-merge | pseudo-merges | Delta | | | | (HEAD^) | | +------------------+-----------------+---------------+---------------------+ | elapsed | 294.1 s | 575.0 s | +280.9 s (+95.5%) | | cycles | 1,365.5 B | 2,686.9 B | +1,321.4 B (+96.8%) | | instructions | 1,389.8 B | 2,546.6 B | +1,156.8 B (+83.2%) | | CPI | 0.983 | 1.055 | +0.073 (+7.4%) | +------------------+-----------------+---------------+---------------------+ This is a particularly poor trade-off, because the time saved by these pseudo-merges during, e.g., $ git rev-list --count --all --objects --use-bitmap-index is only: $ hyperfine -L v true,false -n 'pseudo-merges: {v}' ' GIT_TEST_USE_PSEUDO_MERGES={v} git.compile rev-list --count \ --objects --all --use-bitmap-index ' Benchmark 1: pseudo-merges: true Time (mean ± σ): 2.613 s ± 0.012 s [User: 2.308 s, System: 0.305 s] Range (min … max): 2.594 s … 2.633 s 10 runs Benchmark 2: pseudo-merges: false Time (mean ± σ): 52.205 s ± 0.170 s [User: 51.500 s, System: 0.697 s] Range (min … max): 51.956 s … 52.458 s 10 runs Summary pseudo-merges: true ran 19.98 ± 0.11 times faster than pseudo-merges: false In other words, we pay a nearly ~5 minute penalty to generate pseudo-merge bitmaps, but only save ~50 seconds during traversal. The problem stems from injecting pseudo-merges into the bitmap builder as if they were normal commits. The maximal commit selection algorithm was simply not designed for that case, and performs predictably poorly. The only reason we reused the maximal commit selection routine for pseudo-merges alongside regular non-pseudo-merge commits is because we represent them both as commit objects (where the pseudo-merge commits just represent a made-up commit as opposed to one that actually exists in a repository's object store). Instead, build the regular selected commit bitmaps first, considering only non-pseudo-merge commits in `bitmap_builder_init()`. Once those bitmaps have been stored, build each pseudo-merge bitmap separately and attach its parent and object bitmaps to the corresponding pseudo-merge entry before writing the extension. This keeps the regular bitmap build shaped like the no-pseudo-merge case. The later pseudo-merge fill can still stop at stored selected ancestor bitmaps, so it does not have to rewalk each pseudo-merge closure from scratch. When an existing bitmap has the same pseudo-merge parent set, reuse and remap that whole pseudo-merge bitmap before falling back to fill_bitmap_commit(). This preserves the benefit of stable pseudo-merges while keeping the on-disk format and reader behavior unchanged. As a result, the overhead cost for generating pseudo-merges in the above configuration is much smaller: +------------------+-----------------+---------------+-------------------+ | | no pseudo-merge | pseudo-merges | Delta | | | | (HEAD) | | +------------------+-----------------+---------------+-------------------+ | elapsed | 294.1 s | 328.4 s | +34.3 s (+11.7%) | | cycles | 1,365.5 B | 1,529.3 B | +163.7 B (+12.0%) | | instructions | 1,389.8 B | 1,552.8 B | +163.0 B (+11.7%) | | CPI | 0.983 | 0.985 | +0.002 (+0.2%) | +------------------+-----------------+---------------+-------------------+ Recall that at the start of this series, generating reachability bitmaps took 612.5 seconds *without* pseudo-merges. With this commit, it is still ~46.38% *faster* to generate reachability bitmaps *with* pseudo-merges than it was to generate bitmaps wihtout them at the beginning of this series. The changes to implement this are mostly straightforward. We exclude pseudo-merge commits from the existing bitmap generation, and walk over them in a separate pass, by either reusing an existing on-disk pseudo-merge, or passing the pseudo-merge commit itself back to the existing routine in `fill_bitmap_commit()`. (Note that the routine to build pseudo-merge bitmaps is the same both before and after this change, the difference is only that we do not let psuedo-merges participate in determining the set of maximal commits.) The only wrinkle is that `fill_bitmap_commit()` must be taught to not expect that all tree objects have been parsed, which is the case for any portion of history reachable by one or more pseudo-merge(s), but not by any non-pseudo-merge commit selected for bitmapping. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28pack-bitmap: remember pseudo-merge parentsTaylor Blau1-13/+17
write_pseudo_merges() currently builds an array of temporary bitmaps for the parent set of each pseudo-merge, then serializes those bitmaps later while writing the extension. Move those parent bitmaps onto the corresponding bitmapped_commit entries instead. This keeps the on-disk output unchanged, but gives the parent bitmap the same lifetime and access pattern that later changes will use when pseudo-merge object bitmaps are built before the write step. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28pack-bitmap: sort bitmaps before XORingTaylor Blau1-0/+29
Reachability bitmaps may be stored as XORs against nearby bitmaps, up to 10 away. However, when callers provide selected commits in an arbitrary order, the writer may miss good ancestor/descendant pairs and produce much larger bitmap files without changing query coverage. Sort the selected bitmaps in date order (from oldest to newest) before computing XOR offsets, leaving pseudo-merge bitmaps alone (which we will deal with separately in following commits). On our same testing repository from previous commits, this change shrunk our selection of 1,261 bitmaps from ~635.46 MiB to 176.4 MiB for a ~72.24% reduction in the on-disk size of our *.bitmap file. The time to generate the smaller bitmap file decreased by ~3.69 seconds, though this is likely mostly noise. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28pack-bitmap: cache object positions during fillTaylor Blau2-1/+94
The previous commits removed some redundant work from bitmap generation by avoiding unnecessary tree recursion and by reusing selected bitmaps that have already been computed. Even with those changes in place, there is still an extremely hot path from `fill_bitmap_commit()` and `fill_bitmap_tree()` to translate object IDs into their corresponding bit positions in order to generate their bitmaps. In a small repository, this overhead is not significant. However, in a very large repository (e.g., the one that we have been using as a benchmark over the past several commits with ~57M total objects), the overhead of locating object bit positions (often repeatedly) adds up significantly. Combat this by adding a small, direct-mapped cache to the bitmap writer which maps object IDs to their corresponding bit positions. Size the cache according to the number of objects being written, with fixed lower and upper bounds so small repositories do not pay for a large table and large repositories can avoid most repeated packlist and MIDX lookups. On my machine with (a somewhat outdated) GCC 15.2.0, each entry in the cache is 40 bytes wide: $ pahole -C bitmap_pos_cache_entry pack-bitmap-write.o struct bitmap_pos_cache_entry { struct object_id oid; /* 0 36 */ uint32_t pos; /* 36 4 */ /* size: 40, cachelines: 1, members: 2 */ /* last cacheline: 40 bytes */ }; , and we will allocate up to 2^21 entries for a maximum total of 80 MiB of cache overhead. In our example repository from above and in earlier commits, this results in a ~9.4% reduction in runtime relative to the previous commit: +------------------+-------------+-------------+---------------------+ | | HEAD^ | HEAD | Delta | +------------------+-------------+-------------+---------------------+ | elapsed | 324.8 s | 294.1 s | -30.7 s (-9.4%) | | cycles | 1,508.6 B | 1,365.5 B | -143.0 B (-9.5%) | | instructions | 1,436.6 B | 1,389.8 B | -46.9 B (-3.3%) | | CPI | 1.050 | 0.983 | -0.068 (-6.4%) | +------------------+-------------+-------------+---------------------+ When generating bitmaps on this repository (to produce the above timings), the cache grew to its maximum size of 80 MiB, and resulted in 1.024B cache hits and 59.957M cache misses. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28pack-bitmap: consolidate `find_object_pos()` success pathTaylor Blau1-9/+9
Both sides of `find_object_pos()` report success in the same way by setting the optional `found` out-parameter and return the resolved bitmap position. Prepare for adding more bookkeeping around object-position lookups by storing the result in a local `pos` variable and sharing the success return path between the packlist and MIDX cases. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28pack-bitmap: reuse stored selected bitmapsTaylor Blau1-0/+34
When `fill_bitmap_commit()` reaches an ancestor that was selected for its own bitmap and processed earlier, its object closure is already stored in `writer->bitmaps` as an EWAH bitmap. As a result, walking through that commit's tree and parents again is redundant. Teach `fill_bitmap_commit()` to notice that case. For non-root commits in the walk, look for a stored selected bitmap and OR it into the bitmap being built. If one exists, skip the commit, its tree, and its parents. Building bitmaps from scratch on the same test repository from the previous commits yields a significant speed-up: +------------------+-------------+-------------+---------------------+ | | HEAD^ | HEAD | Delta | +------------------+-------------+-------------+---------------------+ | elapsed | 562.8 s | 324.8 s | -237.9 s (-42.3%) | | cycles | 2,621.3 B | 1,508.6 B | -1,112.7 B (-42.4%) | | instructions | 2,348.9 B | 1,436.6 B | -912.3 B (-38.8%) | | CPI | 1.116 | 1.050 | -0.066 (-5.9%) | +------------------+-------------+-------------+---------------------+ In our testing repository, there are 1,261 commits selected for bitmap coverage, and 1,382 maximal commits induced as a result of that. Of the 1,382 calls made to `fill_bitmap_commit()` (one per maximal commit), 131 of them can be short-circuited at some point during their traversal as a consequence of this change. In large repositories where the cost of filling the bitmap for any individual commit is large, being able to short-circuit even ~9.5% of the calls to `fill_bitmap_commit()` results in a significant savings. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28pack-bitmap: check subtree bits before recursingTaylor Blau1-6/+17
In the previous commit, we adjusted the callers of `fill_bitmap_tree()` to pass in the bit position of the tree they wish to fill. This commit makes use of that information at the call site to avoid setting up a stack frame for fill_bitmap_tree() entirely whenever a tree's bit position is already set. Since this is such a hot path, the avoided cost of setting up and tearing down stack frames for each noop'd call to `fill_bitmap_tree()` is significant: +--------------+-------------+-------------+-------------------+ | | HEAD^ | HEAD | Delta | +--------------+-------------+-------------+-------------------+ | elapsed | 582.4 s | 562.8 s | -19.6 s (-3.4%) | | cycles | 2,713.3 B | 2,621.3 B | -92.0 B (-3.4%) | | instructions | 2,415.5 B | 2,348.9 B | -66.6 B (-2.8%) | | CPI | 1.123 | 1.116 | -0.007 (-0.7%) | +--------------+-------------+-------------+-------------------+ In the same repository as in the previous commit, our timings dropped from ~582.4 seconds down to ~562.77 seconds. While the cycles-per-instruction ratio is basically unchanged, we execute significantly fewer instructions, and correspondingly fewer cycles. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28pack-bitmap: pass object position to `fill_bitmap_tree()`Taylor Blau1-8/+15
In the following commit, callers of `fill_bitmap_tree()` will be required to check the bit corresponding to their tree before calling that function. That change will reduce the overhead of setting up and tearing down stack frames for trees whose bits are already set. To prepare for that change, have callers pass in the tree's bit position in `fill_bitmap_tree()`, which will make the next commit easier to read. In the meantime, this change has a surprising and measurable benefit during bitmap generation, particularly on very large repositories. When processing sub-trees within `fill_bitmap_tree()`, the preimage of this patch did the following: while (tree_entry(&desc, entry)) { switch (object_type(entry.mode)) { case OBJ_TREE: if (fill_bitmap_tree(writer, bitmap, lookup_tree(writer->repo, &entry.oid)) < 0) { /* ... */ } /* ... */ } } , first performing the object lookup via `lookup_tree()`, and then locating its bit position within the recursive call. This patch effectively reorders those two calls so that we first discover the sub-tree's bit position, *then* load its tree. By reordering these two operations, we spend fewer CPU cycles per instruction, likely due to improved CPU dependency/cache/pipeline behavior. Comparing the results of: running `perf stat` before and after this commit, we have: +--------------+-------------+-------------+-------------------+ | | HEAD^ | HEAD | Delta | +--------------+-------------+-------------+-------------------+ | elapsed | 612.5 s | 582.4 s | -30.1 s (-4.9%) | | cycles | 2,857.3 B | 2,713.3 B | -144.0 B (-5.0%) | | instructions | 2,413.2 B | 2,415.5 B | +2.3 B (+0.1%) | | CPI | 1.184 | 1.123 | -0.061 (-5.1%) | +--------------+-------------+-------------+-------------------+ In a large repository with ~4.8M commit, and ~37.1M tree objects this change improves timing from ~612.5 seconds down to ~582.4 seconds, or a ~4.9% improvement. More importantly, the number of CPU cycles spent dropped off significantly as a result of this commit, lowering our cycles-per-instruction ratio by about ~5.1%. Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28doc: promisor: improve acceptFromServer entryChristian Couder1-18/+35
The entry for the `promisor.acceptFromServer` in "Documentation/config/promisor.adoc" has a number of issues: - it's not clear if new remotes and URLs can be created, - it looks like a big block of text, - it's not easy to see all the options, - it's not easy to see which option is the default one, - for "knownName", it says "advertised by the client" instead of "advertised by the server", - it doesn't refer to the new related `acceptFromServerUrl` option. Let's address all these issues by rewording large parts of it and using bullet points for the different options. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28promisor-remote: auto-configure unknown remotesChristian Couder4-13/+340
Previous commits have introduced the `promisor.acceptFromServerUrl` config variable to allowlist some URLs advertised by a server through the "promisor-remote" protocol capability. However the new `promisor.acceptFromServerUrl` mechanism, like the old `promisor.acceptFromServer` mechanism, still requires a remote to already exist in the client's local configuration before it can be accepted. This places a significant manual burden on users to pre-configure these remotes, and creates friction for administrators who have to troubleshoot or manually provision these setups for their teams. To eliminate this burden, let's automatically create a new `[remote]` section in the client's config when a server advertises an unknown remote whose URL matches a `promisor.acceptFromServerUrl` glob pattern. Concretely, let's add four helpers: - sanitize_remote_name(): turn an arbitrary URL-derived string into a valid remote name by replacing non-alphanumeric characters, collapsing runs of '-', and prepending "promisor-auto-". - promisor_remote_name_from_url(): normalize the URL and extract host+port+path to build a human-readable base name, then pass it through sanitize_remote_name(). - configure_auto_promisor_remote(): write the remote.*.url, remote.*.promisor and remote.*.advertisedAs keys to the repo config. - handle_matching_allowed_url(): pick the final name (user-supplied alias or auto-generated), handle collisions by appending "-1", "-2", etc., then call configure_auto_promisor_remote(). Let's also add should_accept_new_remote_url() which reuses the url_matches_accept_list() helper introduced in a previous commit to find a matching pattern, then delegates to handle_matching_allowed_url() to create the remote. And then let's call should_accept_new_remote_url() from the '!item' (unknown remote) branch of should_accept_remote(), setting `reload_config` so that the newly-written config is picked up. Finally let's document all that by: - expanding the `promisor.acceptFromServerUrl` entry to describe auto-creation, the optional "name=" prefix syntax, the "promisor-auto-*" generation rules, and numeric-suffix collision handling, and by - adding a "remote.<name>.advertisedAs" entry to "remote.adoc". Also let's extend the precedence paragraph added by a previous commit to mention this new acceptance path: until now, the only way for `promisor.acceptFromServerUrl` to trigger acceptance was to allow field updates for a known remote. With this commit, it can also trigger auto-creation of a previously-unknown remote whose advertised URL matches the allowlist. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28promisor-remote: trust known remotes matching acceptFromServerUrlChristian Couder4-14/+244
A previous commit introduced the `promisor.acceptFromServerUrl` config variable along with the machinery to parse and validate the URL glob patterns and optional remote name prefixes it contains. However, these URL patterns are not yet tied into the client's acceptance logic. When a promisor remote is already configured locally, its fields (like authentication tokens) may occasionally need to be refreshed by the server. If `promisor.acceptFromServer` is set to the secure default ("None"), these updates are rejected, potentially causing future fetches to fail. To enable such targeted updates for trusted URLs, let's use the URL patterns from `promisor.acceptFromServerUrl` as an additional URL based allowlist. Concretely, let's check the advertised URLs against the URL glob patterns by introducing a new small helper function called url_matches_accept_list(), which iterates over the glob patterns and returns the first matching allowed_url entry (or NULL). The URL matching is done component by component: scheme and port are compared exactly, the host and path are matched with wildmatch(). Before matching, the advertised URL is passed through url_normalize() so that case variations in the scheme/host, percent-encoding tricks, and ".." path segments cannot bypass the allowlist. The username and password components of the URL are intentionally ignored during matching to allow servers to rotate them, though using the 'token' field of the capability is preferred over embedding credentials in the URL. Let's then use this helper in should_accept_remote() so that a known remote whose URL matches the allowlist is accepted. To prepare for this new logic, let's also: - Add an 'accept_urls' parameter to should_accept_remote(). - Replace the BUG() guard in the ACCEPT_KNOWN_URL case with an explicit 'if (accept == ACCEPT_KNOWN_URL) return' and a new BUG() guard in the ACCEPT_NONE case. - Call accept_from_server_url() from filter_promisor_remote() and relax its early return so that the function is entered when `accept_urls` has entries even if `accept == ACCEPT_NONE`. With this, many organizations may only need something like: git config set --global \ promisor.acceptFromServerUrl "https://my-org.com/*" to accept only their own remotes. And if they need to accept additional remotes in some specific repos, they can also set: git config set promisor.acceptFromServer knownUrl and configure the additional remote manually only in the repos where they are needed. Let's then properly document `promisor.acceptFromServerUrl` in "promisor.adoc" as an additive security allowlist for known remotes, including the URL normalization behavior and the component-wise matching, and let's mention it in "gitprotocol-v2.adoc". Also let's clarify in the documentation how `promisor.acceptFromServerUrl` interacts with `promisor.acceptFromServer`: - Precedence: when both options are set, `promisor.acceptFromServerUrl` is consulted first. If a matching pattern leads to acceptance, the remote is accepted regardless of `promisor.acceptFromServer`. Otherwise the decision is left to `promisor.acceptFromServer`. - URL-mismatch guard: even when the advertised URL matches the allowlist, an already-existing client-side remote whose configured URL differs from the advertised one is not accepted through `promisor.acceptFromServerUrl`. `promisor.acceptFromServer=all` and `=knownName` keep their pre-existing, looser semantics. The precedence paragraph is intentionally scoped here to known remotes only (field updates). A following commit that introduces auto-creation of unknown remotes will extend it to cover that case as well. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28promisor-remote: introduce promisor.acceptFromServerUrlChristian Couder2-0/+111
The "promisor-remote" protocol capability allows servers to advertise promisor remotes, but doesn't allow these remotes to be automatically configured on the client. Let's introduce a new `promisor.acceptFromServerUrl` config variable which contains a glob pattern, so that advertised remotes with a URL matching that pattern will be automatically configured. The glob pattern can optionally be prefixed with a remote name which will be used as the name of the new local remote. For now though, let's only introduce the functions to read and validate the glob patterns and the optional prefixes. Checking if the URLs of the advertised remotes match the glob patterns and taking the appropriate action is left for a following commit. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28promisor-remote: add 'local_name' to 'struct promisor_info'Christian Couder1-7/+15
In a following commit, we will store promisor remote information under a remote name different than the one the server advertised. To prepare for this change, let's add a new 'char *local_name' member to 'struct promisor_info', and let's update the related functions. While at it, let's also add a small promisor_info_local_name() helper that returns `local_name` when set, `name` otherwise, and let's use this small helper in promisor_store_advertised_fields() and in the post-loop of filter_promisor_remote() so that lookups against the local repo configuration use the right name. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28urlmatch: add url_normalize_pattern() helperChristian Couder2-0/+17
In a following commit, we will need to normalize a URL glob pattern (which may contain '*' in the host portion) and extract its component offsets (host, path, etc.) for separate matching. Let's export a dedicated helper function url_normalize_pattern() for that purpose. It works like url_normalize(), but passes allow_globs=true to the internal url_normalize_1(), so that '*' characters in the host are accepted rather than rejected. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28urlmatch: change 'allow_globs' arg to boolChristian Couder1-3/+3
The last argument of url_normalize_1() is `char allow_globs` but it is used as a boolean, not as a char. Let's convert it to a `bool`, and while at it convert the two calls to url_normalize_1() so they pass 'true' or 'false' instead of '1' or '0'. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28t5710: simplify 'mkdir X' followed by 'git -C X init'Christian Couder1-4/+2
It's simpler and more efficient to just use `git init client` instead of `mkdir client && git -C client init`. So let's replace the latter with the former. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-27The 9th batchJunio C Hamano1-0/+40
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-27Merge branch 'ps/setup-wo-the-repository'Junio C Hamano84-384/+403
Many uses of the_repository has been updated to use a more appropriate struct repository instance in setup.c codepath. * 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
2026-05-27Merge branch 'ps/odb-in-memory'Junio C Hamano16-118/+854
Add a new odb "in-memory" source that is meant to only hold tentative objects (like the virtual blob object that represents the working tree file used by "git blame"). * ps/odb-in-memory: 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
2026-05-27Merge branch 'jt/odb-transaction-write'Junio C Hamano14-213/+304
ODB transaction interface is being reworked to explicitly handle object writes. * jt/odb-transaction-write: 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 object-file: remove flags from transaction packfile writes odb: update `struct odb_write_stream` read() callback odb/transaction: use pluggable `begin_transaction()` odb: split `struct odb_transaction` into separate header
2026-05-27Merge branch 'tb/incremental-midx-part-3.3'Junio C Hamano17-199/+1908
The repacking code has been refactored and compaction of MIDX layers have been implemented, and incremental strategy that does not require all-into-one repacking has been introduced. * tb/incremental-midx-part-3.3: repack: allow `--write-midx=incremental` without `--geometric` repack: introduce `--write-midx=incremental` repack: implement incremental MIDX repacking packfile: ensure `close_pack_revindex()` frees in-memory revindex builtin/repack.c: convert `--write-midx` to an `OPT_CALLBACK` repack-geometry: prepare for incremental MIDX repacking repack-midx: extract `repack_fill_midx_stdin_packs()` repack-midx: factor out `repack_prepare_midx_command()` midx: expose `midx_layer_contains_pack()` repack: track the ODB source via existing_packs midx: support custom `--base` for incremental MIDX writes midx: introduce `--no-write-chain-file` for incremental MIDX writes midx: use `strvec` for `keep_hashes` midx: build `keep_hashes` array in order midx: use `strset` for retained MIDX files midx-write: handle noop writes when converting incremental chains
2026-05-27Merge branch 'ds/fetch-negotiation-options'Junio C Hamano21-62/+564
The negotiation tip options in "git fetch" have been reworked to allow requiring certain refs to be sent as "have" lines, and to restrict negotiation to a specific set of refs. * ds/fetch-negotiation-options: send-pack: pass negotiation config in push remote: add remote.*.negotiationInclude config fetch: add --negotiation-include option for negotiation negotiator: add have_sent() interface remote: add remote.*.negotiationRestrict config transport: rename negotiation_tips fetch: add --negotiation-restrict option t5516: fix test order flakiness
2026-05-27Merge branch 'rs/use-builtin-add-overflow-explicitly-on-clang'Junio C Hamano2-6/+22
Micro optimization of codepaths that compute allocation sizes carefully. * rs/use-builtin-add-overflow-explicitly-on-clang: use __builtin_add_overflow() in st_add() with Clang strbuf: use st_add3() in strbuf_grow()
2026-05-27Merge branch 'jk/sq-dequote-cleanup'Junio C Hamano2-29/+6
Code simplification. * jk/sq-dequote-cleanup: quote: simplify internals of dequoting quote: drop sq_dequote_to_argv() quote.h: bump strvec forward declaration to the top
2026-05-27Merge branch 'kn/refs-fsck-skip-lock-files'Junio C Hamano2-11/+52
The consistency checks for the files reference backend have been updated to skip lock files earlier, avoiding unnecessary parsing of intermediate files. * kn/refs-fsck-skip-lock-files: refs/files: skip lock files during consistency checks
2026-05-27Merge branch 'ps/t3903-cover-stash-include-untracked'Junio C Hamano1-0/+17
Test coverage has been added to "git stash --include-untracked". * ps/t3903-cover-stash-include-untracked: stash: add coverage for show --include-untracked
2026-05-27Merge branch 'kk/merge-octopus-optim'Junio C Hamano2-14/+14
The logic to determine that branches in an octopus merge are independent has been optimized. * kk/merge-octopus-optim: merge: use repo_in_merge_bases for octopus up-to-date check
2026-05-27Merge branch 'en/batch-prefetch'Junio C Hamano6-1/+371
In a lazy clone, "git cherry" and "git grep" often fetch necessary blob objects one by one from promisor remotes. It has been corrected to collect necessary object names and fetch them in bulk to gain reasonable performance. * en/batch-prefetch: grep: prefetch necessary blobs builtin/log: prefetch necessary blobs for `git cherry` patch-ids.h: add missing trailing parenthesis in documentation comment promisor-remote: document caller filtering contract
2026-05-27Merge branch 'rs/trailer-fold-optim'Junio C Hamano1-10/+5
Code simplification. * rs/trailer-fold-optim: trailer: change strbuf in-place in unfold_value()
2026-05-27Merge branch 'pb/doc-diff-format-updates'Junio C Hamano1-7/+7
Doc updates. * pb/doc-diff-format-updates: diff-format.adoc: mode and hash are 0* for unmerged paths from index only diff-format.adoc: 'git diff-files' prints two lines for unmerged files diff-format.adoc: remove mention of diff-tree specific output
2026-05-27Merge branch 'kk/limit-list-optim'Junio C Hamano1-17/+21
The limit_list() function that is one of the core part of the revision traversal infrastructure has been optimized by replacing its use of linear list with priority queue. * kk/limit-list-optim: revision: use priority queue in limit_list()
2026-05-27Merge branch 'tb/pseudo-merge-bugfixes'Junio C Hamano7-14/+402
Fixes many bugs in pseudo-merge code. * 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
2026-05-27restore: avoid sparse index expansionDerrick Stolee2-10/+63
Teach update_some() to handle sparse directory entries at the tree level rather than expanding the entire sparse index. When iterating a source tree during checkout/restore operations: - If a directory matches a sparse directory entry with the same OID, skip it entirely (no change needed). - If the OID differs and we are in non-overlay mode (e.g., restore --staged), update the sparse directory entry's OID in place. This is semantically correct because non-overlay mode removes paths not in the source tree anyway. - In overlay mode (e.g., checkout <tree> -- .), fall through to recursive descent so individual file entries are preserved correctly. Also switch from index_name_pos() to index_name_pos_sparse() for individual file lookups to avoid triggering ensure_full_index() when the file is already individually tracked in the index. Update the test expectation in t1092 to assert that 'restore --staged' no longer expands the sparse index. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-27t1092: test 'git restore' with sparse indexDerrick Stolee1-0/+50
A user reported that 'git restore --staged .' causes the sparse index to expand. This is somewhat natural because the '.' pathspec means 'check every path'. However, the restore will not update paths marked with the SKIP_WORKTREE bit, so we shouldn't need to process such entries. For now, establish the current behavior, including the sparse index expansion, in the t1092 test case as a baseline. Signed-off-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-26doc: clarify push.default=simple behaviorIvan Baluta1-3/+4
The documentation for the 'simple' push mode currently singles out the centralized workflow, which can cause confusion about its behavior in other scenarios, such as triangular workflows. Clarify that 'simple' always pushes the current branch to a branch of the same name, but only enforces the strict upstream tracking requirement when pushing back to the same remote being pulled from. Suggested-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Ivan Baluta <ivanbaluta.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-26receive-pack: fix updateInstead with core.worktreeAlyssa Ross2-1/+12
Before a8cc594333 (hooks: fix an obscure TOCTOU "did we just run a hook?" race, 2022-03-07), when receive.denyCurrentBranch is set to updateInstead, only one of push_to_checkout() or push_to_deploy() was called. That commit changed to always call push_to_checkout(), and then to call push_to_deploy() if push_to_checkout() didn't run anything. This change didn't take into account that push_to_checkout() had a side effect of modifying env, and that modified env broke updating the worktree in push_to_deploy() if core.worktree was configured. To fix this, only mutate the environment used inside push_to_commit(), rather than the environment that might later be passed to push_to_deploy(). Signed-off-by: Alyssa Ross <hi@alyssa.is> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-26commit-reach: replace queue_has_nonstale() scan with O(1) trackingKristofer Karlsson1-31/+65
paint_down_to_common() and ahead_behind() call queue_has_nonstale() on every iteration to decide whether to continue the walk. queue_has_nonstale() performs a linear scan of the priority queue, making the overall walk O(n*m) where n is the number of commits walked and m is the queue size. Introduce 'struct nonstale_queue', a thin wrapper around prio_queue that maintains a 'max_nonstale' pointer — the lowest-priority (oldest) non-stale commit seen so far. When this commit is popped, every remaining queue entry is known to be stale, so the walk can stop. This reduces the per-iteration termination check from O(m) to O(1). Uses <= 0 (not < 0) when comparing priorities so that among distinct commits with equal priority (same generation and timestamp) the last-enqueued one is tracked. Since prio_queue breaks ties by insertion order, this ensures max_nonstale is always the last in its priority class to be popped, making pointer equality on pop sufficient for correctness. The previous commit's ENQUEUED deduplication guarantees each commit appears at most once in the queue, which is required for the pointer equality check to be unambiguous. On a large monorepo (3.7M commits), this yields ~2x end-to-end speedup for merge-base calculations on deep import branches. Profiling shows paint_down_to_common() drops from 50% to 4% of total runtime (~27x faster), with the remaining time in commit graph lookups and heap operations: Before: 8536ms / 5757ms / 4743ms (three test cases) After: 3956ms / 4383ms / 1927ms Suggested-by: Jeff King <peff@peff.net> Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-26commit-reach: deduplicate queue entries in paint_down_to_commonKristofer Karlsson2-6/+23
paint_down_to_common() can enqueue the same commit multiple times when it is reached through different parents with different flag combinations. Add an ENQUEUED flag to track whether a commit is currently in the priority queue, and skip it if already present. Introduce prio_queue_put_dedup() and prio_queue_get_dedup() wrappers that manage the ENQUEUED flag on enqueue and dequeue. This change is performance-neutral on its own: the O(n) queue_has_nonstale() scan still dominates the per-iteration cost. However, the deduplication guarantee (each commit appears in the queue at most once) is a prerequisite for the next commit, which replaces that scan with O(1) tracking. Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-26object.h: fix stale entries in object flag allocation tableKristofer Karlsson1-2/+3
Update three stale entries found during an audit of the flag allocation table: - sha1-name.c was renamed to object-name.c - builtin/show-branch.c uses bits 0 and 2-28, not 0-26 (REV_SHIFT=2, MAX_REVS=FLAG_BITS-REV_SHIFT=27) - negotiator/skipping.c uses bits 2-5 like negotiator/default.c (ADVERTISED on bit 3 instead of COMMON_REF) Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25doc: convert git-imap-send synopsis and options to new styleJean-Noël Avila2-27/+27
Convert git-imap-send from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Backtick-quote all OPTIONS terms - Backtick-quote all config keys in config/imap.adoc - Backtick-quote bare config key references in prose Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25doc: convert git-apply synopsis and options to new styleJean-Noël Avila2-68/+74
Convert git-apply from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Backtick-quote all OPTIONS terms and config keys in config/apply.adoc - Convert single-quoted inline commands ('git apply', 'diff', etc.) - Wrap standalone placeholders in underscores (<n>, <root>, <action>) - Backtick-quote `*.rej` and GNU `patch` tool references Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25doc: convert git-am synopsis and options to new styleJean-Noël Avila4-72/+72
Convert git-am from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Backtick-quote all OPTIONS terms - Convert inline man page refs - Convert inline command refs - Convert prose placeholders: Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25doc: convert git-grep synopsis and options to new styleJean-Noël Avila2-116/+116
Convert git-grep.adoc from [verse]/single-quote style to the modern synopsis-block style: - Replace [verse] with [synopsis] in SYNOPSIS block - Change 'git grep' to git grep (no single quotes) - Backtick-quote all OPTIONS terms - Convert inline man page refs: grep(1) -> `grep`(1) - Convert inline command refs: 'git diff' -> `git diff` - Convert prose placeholders: <file> -> _<file>_ Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25doc: git bisect: clarify the usage of the synopsis vs actual commandJean-Noël Avila1-10/+9
The difference between a synopsis and an actual command is that the synopsis is a more abstract representation of the command, which may include placeholders for arguments and options. The actual command is the specific instance of the command with all the arguments and options filled in. The formatting of an actual command is a code block, with the command prefixed by a dollar sign ($) to indicate that it is a command to be run in the terminal. It can also include comments with a hash sign (#) to explain the command or provide additional information, just like in a regular terminal session. Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25doc: convert git-bisect to synopsis styleJean-Noël Avila2-48/+48
Convert Documentation/git-bisect.adoc to the modern synopsis style. - Replace [verse] with [synopsis] in the SYNOPSIS block - Remove single quotes around command names in the synopsis - Use backticks for inline commands, options, refs, and special values - Apply [synopsis] attribute to in-body command-form code blocks - Format OPTIONS entries with backtick-quoted terms and direct - Add synopsis-style formatting to listing blocks - Format man page references as `command`(N) Signed-off-by: Jean-Noël Avila <jn.avila@free.fr> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25SubmittingPatches: proactively monitor GHCI pagesJunio C Hamano1-0/+11
Even those contributors who do not come from GGG and do not first push their changes to their repositories on GitHub with CI enabled, can still monitor the CI runs triggered by integration of their topic to 'seen' and other branches to notice a breakage their topic caused to the system. Encourage them to help the project by keeping an eye on these CI runs. Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25The 8th batchJunio C Hamano1-0/+19
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-25Merge branch 'jk/dumb-http-alternate-fix'Junio C Hamano2-1/+21
The HTTP walker misinterpreted the alternates file that gives an absolute path when the server URL does not have the final slash (i.e., "https://example.com" not "https://example.com/"). * jk/dumb-http-alternate-fix: http: handle absolute-path alternates from server root
2026-05-25Merge branch 'jk/pretty-no-strbuf-presizing'Junio C Hamano1-1/+0
Remove ineffective strbuf presizing that would have computed an allocation that would not have fit in the available memory anyway, or too small due to integer wraparound to cause immediate automatic growing. * jk/pretty-no-strbuf-presizing: pretty: drop strbuf pre-sizing from add_rfc2047()
2026-05-25Merge branch 'ag/sequencer-remove-unused-struct-member'Junio C Hamano1-2/+0
Code clean-up. * ag/sequencer-remove-unused-struct-member: sequencer: remove todo_add_branch_context.commit