summaryrefslogtreecommitdiff
AgeCommit message (Collapse)AuthorFilesLines
2026-05-15odb/source-inmemory: implement `freshen_object()` callbackPatrick Steinhardt1-0/+10
Implement the `freshen_object()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `count_objects()` callbackPatrick Steinhardt1-0/+20
Implement the `count_objects()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `find_abbrev_len()` callbackPatrick Steinhardt1-0/+39
Implement the `find_abbrev_len()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `for_each_object()` callbackPatrick Steinhardt1-16/+72
Implement the `for_each_object()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: convert to use oidtreePatrick Steinhardt2-31/+54
The in-memory source stores its objects in a simple array that we grow as needed. This has a couple of downsides: - The object lookup is O(n). This doesn't matter in practice because we only store a small number of objects. - We don't have an easy way to iterate over all objects in lexicographic order. - We don't have an easy way to compute unique object ID prefixes. Refactor the code to use an oidtree instead. This is the same data structure used by our loose object source, and thus it means we get a bunch of functionality for free. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15oidtree: add ability to store dataPatrick Steinhardt5-12/+68
The oidtree data structure is currently only used to store object IDs, without any associated data. So consequently, it can only really be used to track which object IDs exist, and we can use the tree structure to efficiently operate on OID prefixes. But there are valid use cases where we want to both: - Store object IDs in a sorted order. - Associated arbitrary data with them. Refactor the oidtree interface so that it allows us to store arbitrary payloads within the respective nodes. This will be used in the next commit. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15cbtree: allow using arbitrary wrapper structures for nodesPatrick Steinhardt3-34/+41
The cbtree subsystem allows the user to store arbitrary data in a prefix-free set of strings. This is used by us to store object IDs in a way that we can easily iterate through them in lexicograph order, and so that we can easily perform lookups with shortened object IDs. In its current form, it is not easily possible to store arbitrary data with the tree nodes. There are a couple of approaches such a caller could try to use, but none of them really work: - One may embed the `struct cb_node` in a custom structure. This does not work though as `struct cb_node` contains a flex array, and embedding such a struct in another struct is forbidden. - One may use a `union` over `struct cb_node` and ones own data type, which _is_ allowed even if the struct contains a flex array. This does not work though, as the compiler may align members of the struct so that the node key would not immediately start where the flex array starts. - One may allocate `struct cb_node` such that it has room for both its key and the custom data. This has the downside though that if the custom data is itself a pointer to allocated memory, then the leak checker will not consider the pointer to be alive anymore. Refactor the cbtree to drop the flex array and instead take in an explicit offset for where to find the key, which allows the caller to embed `struct cb_node` is a wrapper struct. Note that this change has the downside that we now have a bit of padding in our structure, which grows the size from 60 to 64 bytes on a 64 bit system. On the other hand though, it allows us to get rid of the memory copies that we previously had to do to ensure proper alignment. This seems like a reasonable tradeoff. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `write_object_stream()` callbackPatrick Steinhardt1-0/+40
Implement the `write_object_stream()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `write_object()` callbackPatrick Steinhardt2-14/+27
Implement the `write_object()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `read_object_stream()` callbackPatrick Steinhardt1-0/+52
Implement the `read_object_stream()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `read_object_info()` callbackPatrick Steinhardt2-38/+54
Implement the `read_object_info()` callback function for the in-memory source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb: fix unnecessary call to `find_cached_object()`Patrick Steinhardt1-2/+1
The function `odb_pretend_object()` writes an object into the in-memory object database source. The effect of this is that the object will now become readable, but it won't ever be persisted to disk. Before storing the object, we first verify whether the object already exists. This is done by calling `odb_has_object()` to check all sources, followed by `find_cached_object()` to check whether we have already stored the object in our in-memory source. This is unnecessary though, as `odb_has_object()` already checks the in-memory source transitively via: - `odb_has_object()` - `odb_read_object_info_extended()` - `do_oid_object_info_extended()` - `find_cached_object()` Drop the explicit call to `find_cached_object()`. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/source-inmemory: implement `free()` callbackPatrick Steinhardt3-22/+24
Implement the `free()` callback function for the "in-memory" source. Note that this requires us to define `struct cached_object_entry` in "odb/source-inmemory.h", as it is accessed in both "odb.c" and "odb/source-inmemory.c" now. This will be fixed in subsequent commits though. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb: introduce "in-memory" sourcePatrick Steinhardt7-10/+67
Next to our typical object database sources, each object database also has an implicit source of "cached" objects. These cached objects only exist in memory and some use cases: - They contain evergreen objects that we expect to always exist, like for example the empty tree. - They can be used to store temporary objects that we don't want to persist to disk, which is used by git-blame(1) to create a fake worktree commit. Overall, their use is somewhat restricted though. For example, we don't provide the ability to use it as a temporary object database source that allows the user to write objects, but discard them after Git exists. So while these cached objects behave almost like a source, they aren't used as one. This is about to change over the following commits, where we will turn cached objects into a new "in-memory" source. This will allow us to use it exactly the same as any other source by providing the same common interface as the "files" source. For now, the in-memory source only hosts the cached objects and doesn't provide any logic yet. This will change with subsequent commits, where we move respective functionality into the source. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15Merge branch 'jt/odb-transaction-write' into ps/odb-in-memoryJunio C Hamano14-213/+304
* 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-15odb/transaction: make `write_object_stream()` pluggableJustin Tobler3-10/+38
How an ODB transaction handles writing objects is expected to vary between implementations. Introduce a new `write_object_stream()` callback in `struct odb_transaction` to make this function pluggable. Rename `index_blob_packfile_transaction()` to `odb_transaction_files_write_object_stream()` and wire it up for use with `struct odb_transaction_files` accordingly. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15object-file: generalize packfile writes to use odb_write_streamJustin Tobler1-25/+29
The `index_blob_packfile_transaction()` function streams blob data directly from an fd. This makes it difficult to reuse as part of a generic transactional object writing interface. Refactor the packfile write path to operate on a `struct odb_write_stream`, allowing callers to supply data from arbitrary sources. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15object-file: avoid fd seekback by checking object size upfrontJustin Tobler1-61/+25
In certain scenarios, Git handles writing blobs that exceed "core.bigFileThreshold" differently by streaming the object directly into a packfile. When there is an active ODB transaction, these blobs are streamed to the same packfile instead of using a separate packfile for each. If "pack.packSizeLimit" is configured and streaming another object causes the packfile to exceed the configured limit, the packfile is truncated back to the previous object and the object write is restarted in a new packfile. This works fine, but requires the fd being read from to save a checkpoint so it becomes possible to rewind the input source via seeking back to a known offset at the beginning. In a subsequent commit, blob streaming is converted to use `struct odb_write_stream` as a more generic input source instead of an fd which doesn't provide a mechanism for rewinding. For this use case though, rewinding the fd is not strictly necessary because the inflated size of the object is known and can be used to approximate whether writing the object would cause the packfile to exceed the configured limit prior to writing anything. These blobs written to the packfile are never deltified thus the size difference between what is written versus the inflated size is due to zlib compression. While this does prevent packfiles from being filled to the potential maximum is some cases, it should be good enough and still prevents the packfile from exceeding any configured limit. Use the inflated blob size to determine whether writing an object to a packfile will exceed the configured "pack.packSizeLimit". Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15object-file: remove flags from transaction packfile writesJustin Tobler3-50/+136
The `index_blob_packfile_transaction()` function handles streaming a blob from an fd to compute its object ID and conditionally writes the object directly to a packfile if the INDEX_WRITE_OBJECT flag is set. A subsequent commit will make these packfile object writes part of the transaction interface. Consequently, having the object write be conditional on this flag is a bit awkward. In preparation for this change, introduce a dedicated `hash_blob_stream()` helper that only computes the OID from a `struct odb_write_stream`. This is invoked by `index_fd()` instead when the INDEX_WRITE_OBJECT is not set. The object write performed via `index_blob_packfile_transaction()` is made unconditional accordingly. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb: update `struct odb_write_stream` read() callbackJustin Tobler5-20/+44
The `read()` callback used by `struct odb_write_stream` currently returns a pointer to an internal buffer along with the number of bytes read. This makes buffer ownership unclear and provides no way to report errors. Update the interface to instead require the caller to provide a buffer, and have the callback return the number of bytes written to it or a negative value on error. While at it, also move the `struct odb_write_stream` definition to "odb/streaming.h". Call sites are updated accordingly. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb/transaction: use pluggable `begin_transaction()`Justin Tobler1-2/+2
Each ODB source is expected to provide an ODB transaction implementation that should be used when starting a transaction. With d6fc6fe6f8 (odb/source: make `begin_transaction()` function pluggable, 2026-03-05), the `struct odb_source` now provides a pluggable callback for beginning transactions. Use the callback provided by the ODB source accordingly. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15odb: split `struct odb_transaction` into separate headerJustin Tobler12-56/+74
The current ODB transaction interface is colocated with other ODB interfaces in "odb.{c,h}". Subsequent commits will expand `struct odb_transaction` to support write operations on the transaction directly. To keep things organized and prevent "odb.{c,h}" from becoming more unwieldy, split out `struct odb_transaction` into a separate header. Signed-off-by: Justin Tobler <jltobler@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15revision: use priority queue in limit_list()Kristofer Karlsson1-17/+21
limit_list() maintains a date-sorted work queue of commits using a linked list with commit_list_insert_by_date() for insertion. Each insertion walks the list to find the right position — O(n) per insert. In repositories with merge-heavy histories, the symmetric difference can contain thousands of commits, making this O(n) insertion the dominant cost. Replace the sorted linked list with a prio_queue (binary heap). This gives O(log n) insertion and O(log n) extraction instead of O(n) insertion and O(1) extraction, which is a net win when the queue is large. The still_interesting() and everybody_uninteresting() helpers are updated to scan the prio_queue's contiguous array instead of walking a linked list. process_parents() already accepts both a commit_list and a prio_queue parameter, so the change in limit_list() simply switches which one is passed. Benchmark: git rev-list --left-right --count HEAD~N...HEAD Repository: 2.3M commits, merge-heavy DAG (monorepo) Best of 5 runs, times in seconds: commits in symmetric diff baseline patched speedup -------------- -------- ------- ------- 10 0.01 0.01 1.0x 50 0.01 0.01 1.0x 3751 21.23 8.49 2.5x 4524 21.70 8.29 2.6x 10130 20.10 6.65 3.0x No change for small traversals; 2.5-3.0x faster when the queue grows to thousands of commits. Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15grep: prefetch necessary blobsElijah Newren2-0/+201
In partial clones, `git grep` fetches necessary blobs on-demand one at a time, which can be very slow. In partial clones, add an extra preliminary walk over the tree similar to grep_tree() which collects the blobs of interest, and then prefetches them. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15builtin/log: prefetch necessary blobs for `git cherry`Elijah Newren2-0/+158
In partial clones, `git cherry` fetches necessary blobs on-demand one at a time, which can be very slow. We would like to prefetch all necessary blobs upfront. To do so, we need to be able to first figure out which blobs are needed. `git cherry` does its work in a two-phase approach: first computing header-only IDs (based on file paths and modes), then falling back to full content-based IDs only when header-only IDs collide -- or, more accurately, whenever the oidhash() of the header-only object_ids collide. patch-ids.c handles this by creating an ids->patches hashmap that has all the data we need, but the problem is that any attempt to query the hashmap will invoke the patch_id_neq() function on any colliding objects, which causes the on-demand fetching. Insert a new prefetch_cherry_blobs() function before checking for collisions. Use a temporary replacement on the ids->patches.cmpfn in order to enumerate the blobs that would be needed without yet fetching them, and then fetch them all at once, then restore the old ids->patches.cmpfn. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15patch-ids.h: add missing trailing parenthesis in documentation commentElijah Newren1-1/+1
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15promisor-remote: document caller filtering contractElijah Newren1-0/+11
promisor_remote_get_direct() does not, on its happy path, filter out OIDs that are already present in the local object store: every OID the caller supplies is written to the fetch subprocess's stdin and ends up in the response pack. The only filtering it performs is in remove_fetched_oids(), and that only runs after a fetch failure when falling back to a different configured promisor remote. Almost every existing caller already filters locally-present OIDs out itself (typically with odb_read_object_info_extended() and OBJECT_INFO_FOR_PREFETCH, or odb_has_object() with no fetch flag). But the existing API comment does not state this expectation, so a new caller is easy to write incorrectly (I missed this originally and wrote two problematic callers). Omitting the filter still "works" in the sense that the desired objects end up local, but it silently makes the fetch request -- and the response pack -- larger than necessary, defeating part of the point of batching. Spell the contract out so future callers know to filter (and deduplicate) themselves, and point them at the helpers they should use to check local presence without accidentally triggering a lazy fetch. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-14rev-parse: use selected alternate terms to look up refsJonas Rebmann2-2/+38
git rev-parse --bisect does not work when alternate bisect terms are used, simply listing no revisions at all. This is because a such bisect using e.g. "old" and "new" in place of "good" and "bad" will name refs "refs/bisect/old" (or new) accordingly so the hardcoded "refs/bisect/bad" (and good) yields no results in a bisect using alternate terms. Use the current bisect_terms to make rev-parse --bisect work in an alternate term bisect. Signed-off-by: Jonas Rebmann <kernel@schlaraffenlan.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-14bisect: print bisect terms in single quotesJonas Rebmann3-77/+77
As bisect terms can be arbitrarily chosen, they have been quoted in some status messages, and in even more by translators. To make the role of bisect terms more clear, including in translations, and for consistency, 'enquote' all occurrences of bisect terms in status messages. Signed-off-by: Jonas Rebmann <kernel@schlaraffenlan.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-14bisect: use selected alternate terms in status outputJonas Rebmann2-12/+27
Alternate bisect terms are helpful when the terms "good" and "bad" are confusing such as when bisecting for the resolution of an issue (the first good commit) rather than the introduction of a regression. These terms must be used when marking a commit (e.g. `git bisect new`), they will be used in reference names (e.g. refs/bisect/new) and they are used in parts of git's log output such as "<sha> was both old and new" in git bisect skip's output. However, hardcoded "good"/"bad" terms are still used in a few status messages and can cause confusion about the status of the bisect such as: $ git bisect old [sha] is the first new commit or about the required action such as: status: waiting for bad commit, 1 good commit known $ git bisect bad error: Invalid command: you're currently in a new/old bisect fatal: unknown command: 'bad' This commit updates all remaining output messages which use hardcoded "good" and "bad" terms to use the selected terms consistently across the bisect output and adds tests. Signed-off-by: Jonas Rebmann <kernel@schlaraffenlan.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-14hex: add and use strbuf_add_oid_hex()René Scharfe14-17/+37
Add a function for adding the full hexadecimal hash value of an object ID to a strbuf. It's thread-safe and slightly more efficient than using strbuf_addstr() with oid_to_hex() because it doesn't have to determine the length of the string or copy it from the intermediate static buffer. Add and apply a semantic patch to use it throughout the code base. I get a tiny speedup for git log showing a single hash per commit: Benchmark 1: ./git_main log --format=%H Time (mean ± σ): 91.2 ms ± 0.7 ms [User: 51.9 ms, System: 38.6 ms] Range (min … max): 89.8 ms … 92.6 ms 31 runs Benchmark 2: ./git log --format=%H Time (mean ± σ): 90.5 ms ± 0.7 ms [User: 51.0 ms, System: 38.8 ms] Range (min … max): 89.2 ms … 92.3 ms 32 runs Summary ./git log --format=%H ran 1.01 ± 0.01 times faster than ./git_main log --format=%H Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13run-command: honor "gc.auto" for auto-maintenancePatrick Steinhardt2-3/+32
The "gc.auto" configuration has traditionally been used to turn off running git-gc(1) as part of our auto-maintenance. We have eventually switched over to git-maintenance(1) in a95ce12430 (maintenance: replace run_auto_gc(), 2020-09-17), and with 1942d48380 (maintenance: optionally skip --auto process, 2020-08-28) we have introduced "maintenance.auto" to control whether or not to run auto-maintenance. At that point though we still shelled out to git-gc(1) internally. So if "gc.auto=0" was set we would still _execute_ git-maintenance(1), but the command would have exited fast because git-gc(1) itself knew to honor the config key. This has recently changed though, as we have adapted the default maintenance strategy to not use git-gc(1) anymore. The consequence is that "gc.auto=0" doesn't have an effect anymore, which is a somewhat surprising change in behaviour for our users. Adapt `run_auto_maintenance()` so that it knows to also read "gc.auto", similar to how it also reads both "maintenance.autoDetach" and "gc.autoDetach". Reported-by: Jean-Christophe Manciot <actionmystique@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13builtin/maintenance: fix locking with "--detach"Patrick Steinhardt5-1/+111
When running git-maintenance(1), we create a lockfile that is supposed to keep other maintenance processes from running at the same time. This lockfile is broken though in case the "--detach" flag is passed: the lockfile is created by the parent process and will be cleaned up either manually or on exit. But when detaching, the parent will exit before all of the background maintenance tasks have been run, and consequently the lock only covers a smaller part of the whole maintenance process. Fix this bug by reassigning all tempfiles from the parent process to the child process when daemonizing so that it becomes the responsibility of the child to clean them up. Note that this is a broader fix, as we now always reassign tempfiles when daemonizing. This is a natural consequence of the semantics of `daemonize()` though, as it essentially promises to continue running the current process in the background. It is thus sensible to have that function perform the whole dance of assigning resources to the child process, including tempfiles. There's only a single other caller in "daemon.c", but that process doesn't create any tempfiles before the call to `daemonize()` and is thus not impacted by this change. Reported-by: Jean-Christophe Manciot <actionmystique@gmail.com> Helped-by: Jeff King <peff@peff.net> Helped-by: Derrick Stolee <stolee@gmail.com> Co-authored-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Taylor Blau <me@ttaylorr.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13Merge branch 'ps/clang-w-glibc-2.43-and-_Generic'Junio C Hamano2-0/+13
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
2026-05-13ignore: note info/exclude lives in GIT_COMMON_DIR, not GIT_DIRD. Ben Knoble6-13/+13
gitignore(5) says that the per-repository ignore file is $GIT_DIR/info/exclude, but in a worktree that is not the case: git rev-parse --git-path info/exclude /path/to/main/worktree/.git/info/exclude git rev-parse --git-common-dir /path/to/main/worktree/.git We actually use $GIT_COMMON_DIR/info/exclude. Adjust the documentation and some code comments to say so. Signed-off-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13parse-options: clarify what "negated" means for PARSE_OPT_NONEGMichael Montalbo1-0/+1
The documentation says the flag prevents an option from being "negated" without specifying what that means. Add a parenthetical to clarify that it rejects the "--no-<option>" form. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13xdiff: guard against negative context lengthsMichael Montalbo1-4/+12
The xdemitconf_t fields ctxlen and interhunkctxlen are typed as long (signed), but negative values are not meaningful for context line counts. Unlike the diff_options fields changed in the previous two commits, these cannot be converted to unsigned because the xdiff arithmetic relies on signed subtraction: s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); If ctxlen were unsigned long, the signed operand would be implicitly converted to unsigned, and the subtraction would wrap to a large positive value when i1 < ctxlen, defeating the XDL_MAX clamp. The signed type is required for correct context-window calculations. The previous two commits reject negative values at the parse layer for --inter-hunk-context and -U/--unified, so negative values should no longer reach xdiff in normal use. Add BUG() guards at the top of xdl_get_hunk() as defense in depth to catch programming errors in current or future callers that bypass option parsing. xdl_get_hunk() is called by both xdl_emit_diff() and xdl_call_hunk_func(), so a single guard covers all xdiff consumers. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13diff: reject negative values for -U/--unifiedMichael Montalbo3-5/+14
Passing a negative value to -U is silently accepted and produces corrupt unified diff output with malformed hunk headers: $ git log -1 -p -U-500 -- GIT-VERSION-GEN | grep '^@@' @@ -503,999- +503,999- @@ Line 503 of a 106-line file, count "999-" is not a valid integer. The config variable diff.context already rejects negative values, but the command line callback diff_opt_unified() uses strtol() with no range check. Change the type of diff_options.context and its static default from int to unsigned int, matching the change to interhunkcontext in the previous commit. The type change requires reworking the callback and config parsing to validate in a local variable before assigning to the now-unsigned field. Unlike --inter-hunk-context which could be converted to OPT_UNSIGNED, -U needs OPT_CALLBACK_F for PARSE_OPT_OPTARG (bare -U with no value enables patch output). Add a range check in the callback instead. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13diff: reject negative values for --inter-hunk-contextMichael Montalbo3-8/+13
Negative values for --inter-hunk-context produce structurally invalid diff output with overlapping hunks: $ git log -1 -p -U3 --inter-hunk-context=-100 791aeddfa2 \ -- git-compat-util.h | grep '^@@' @@ -110,6 +110,9 @@ @@ -115,6 +118,9 @@ @@ -116,6 +122,7 @@ Hunk 1 covers lines 110-115, hunk 2 starts at 115 (overlap), hunk 3 starts at 116 (overlaps both). The resulting patch cannot be applied. The config variable diff.interHunkContext already rejects negative values, but the command line option does not. Change the type of diff_options.interhunkcontext and its static default from int to unsigned int, and switch the option parser from OPT_INTEGER_F to OPT_UNSIGNED. This rejects negative values at parse time via git_parse_unsigned() and enforces the correct type at compile time via BARF_UNLESS_UNSIGNED. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13http: handle absolute-path alternates from server rootJeff King2-1/+21
When a dumb http server reports alternates with an absolute path, we try to paste that onto the root of the URL we're trying to fetch from. So if we go to "http://example.com/path/to/child.git" and it tells us about an alternate at "/parent.git", we'll hit "http://example.com/parent.git". But there's a bug in computing the base when the URL does not have any path component at all, like "http://example.com". When looking for the first slash after the host, strchr() returns NULL, and we compute a nonsense value for the length of the host portion. And then when we use that length to copy the base of the URL into a strbuf, we're likely to fail. The security implications are minimal here. We store the nonsense length ("serverlen") as an int, so on a 64-bit system it may effectively be anything (it is zero minus a 64-bit heap pointer, then truncated to 32-bits and stuffed into a signed value). When we feed that length to strbuf_add(), it is cast into a size_t and one of four things will happen: 1. If serverlen was negative, it will turn into a very large positive value and strbuf_add() will fail to allocate, ending the program. Ditto if serverlen was positive but just very large. This doesn't really get an attacker anything; the victim will just fail to clone their evil repo. 2. If serverlen was small enough, we'll successfully extend the target strbuf, and then copy an arbitrary set of bytes from "base". And then one of these is true: a. That set of bytes is much larger than the length of the "base" string. This is an out-of-bounds read, but there's no out-of-bounds write, since the strbuf code both allocates and copies using the same size_t. This is likely to cause a segfault as we try to read unmapped pages of memory. b. Like (2a), but if the set of bytes is small enough we might not segfault. We might read random memory from the process and copy it into the "target" strbuf. What happens then? We know that "base" ends with a NUL terminator, which will be copied into "target" as well. So even though target.len might be 1000 bytes (or whatever), when interpreted as a NUL-terminated string, target.buf is still the exact same string as "base". And that's all we ever do with target: pass it around as a C string, and then eventually strbuf_detach() it to become a C string. So even though there was arbitrary memory copied into the strbuf, we never access it. c. The other interesting case is when serverlen is actually _shorter_ than the length of base. And there we truncate the string. Probably in a way that makes it totally invalid, but if you were very unlucky you could turn something like: http://victim.com.evil.domain:8000 into: http://victim.com Which looks like the start of a redirect attack, except that the attacker could just have written "http://victim.com" in the first place! Either way we feed it to is_alternate_allowed(), which is where we check redirect and protocol rules. I think we can just treat this like a regular bug. And it's quite a weird setup in the first place, as it implies that the root of the web server is serving a repository (i.e., that you can get something useful from "http://example.com/info/refs"). The bug has been there since b3661567cf ([PATCH] Add support for alternates in HTTP, 2005-09-14) without anybody noticing. I kind of doubt anybody really cares about making this work, but it's easy enough to do so: the host-portion of the URL ends at either the first slash or the end-of-string. So we can just replace strchr() with strchrnul(). The test setup is a little gross, as we take over the httpd document root by shoving our bare-repo components into it. But it demonstrates the problem and shows that our solution actually allows the alternate to function, if the server is configured to allow it. Reported-by: slonkazoid <slonkazoid@slonk.ing> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13pretty: drop strbuf pre-sizing from add_rfc2047()Jeff King1-1/+0
At the top of add_rfc2047() we do this: strbuf_grow(sb, len * 3 + strlen(encoding) + 100); where "len" is the size of the header (like an author name) we are about to encode into the buffer. This pre-sizing is purely an optimization; we use strbuf_addf() and friends to actually write into the buffer, and they will grow the buffer as necessary. But there's a problem with the code above: the input can be arbitrarily large, so we might overflow a size_t while doing that computation, ending up with a too-small allocation request. Overflowing requires an impractically large input on a 64-bit system, but is easy to demonstrate on a 32-bit system with a commit whose author name is ~1.4GB. Because this pre-sizing is just an optimization, there's no real harm. We'll start with a smaller buffer and grow it as necessary. But it _looks_ like a vulnerability, since some other code may pre-size a strbuf and then write directly into its buffer. So it's worth avoiding the overflow in the first place. The obvious way to do that is via checked operations like st_add() and friends. But taking a step back, is this pre-sizing actually helping anything? The computation goes all the way back to 4234a76167 (Extend --pretty=oneline to cover the first paragraph,, 2007-06-11), but back then we really were sizing the array to write into directly! In 674d172730 (Rework pretty_print_commit to use strbufs instead of custom buffers., 2007-09-10) that switched to a strbuf, and at that point it was a pure optimization. Is the optimization helping? I don't think so. Even for a gigantic case like the 1.4GB author name, I couldn't measure any slowdown when removing it. And most input will be much smaller, and added to a running strbuf containing the rest of the email-header output. We can just rely on strbuf's usual amortized-linear growth. So deleting the line seems like the best way to go. It eliminates the integer overflow and makes the code a tiny bit simpler. Reported-by: Luke Martin <lmartin@paramenoeng.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13ls-tree: use strbuf_add_uint()René Scharfe1-6/+9
Speed up printing of objectsize values by using the specialized function strbuf_add_uint() as well as strbuf_insert() for padding instead of the general-purpose function strbuf_addf(). Here are the numbers I get when listing objects in the Linux kernel repo: Benchmark 1: ./git_main -C ../linux ls-tree -r --format='%(objectsize)' HEAD Time (mean ± σ): 294.4 ms ± 0.4 ms [User: 231.5 ms, System: 59.4 ms] Range (min … max): 293.9 ms … 295.0 ms 10 runs Benchmark 2: ./git -C ../linux ls-tree -r --format='%(objectsize)' HEAD Time (mean ± σ): 291.2 ms ± 0.4 ms [User: 227.9 ms, System: 62.1 ms] Range (min … max): 290.6 ms … 292.0 ms 10 runs Benchmark 3: ./git_main -C ../linux ls-tree -r --format='%(objectsize:padded)' HEAD Time (mean ± σ): 295.3 ms ± 0.6 ms [User: 232.0 ms, System: 59.6 ms] Range (min … max): 294.3 ms … 296.3 ms 10 runs Benchmark 4: ./git -C ../linux ls-tree -r --format='%(objectsize:padded)' HEAD Time (mean ± σ): 291.9 ms ± 0.4 ms [User: 228.5 ms, System: 61.5 ms] Range (min … max): 291.2 ms … 292.3 ms 10 runs Summary ./git -C ../linux ls-tree -r --format='%(objectsize)' HEAD ran 1.00 ± 0.00 times faster than ./git -C ../linux ls-tree -r --format='%(objectsize:padded)' HEAD 1.01 ± 0.00 times faster than ./git_main -C ../linux ls-tree -r --format='%(objectsize)' HEAD 1.01 ± 0.00 times faster than ./git_main -C ../linux ls-tree -r --format='%(objectsize:padded)' HEAD Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13ls-files: use strbuf_add_uint()René Scharfe1-6/+9
Speed up printing of objectsize values by using the specialized function strbuf_add_uint() as well as strbuf_insert() for padding instead of the general-purpose function strbuf_addf(). Here are the numbers I get when listing files in the Linux kernel repo: Benchmark 1: ./git_main -C ../linux ls-files --format='%(objectsize)' Time (mean ± σ): 257.3 ms ± 0.4 ms [User: 197.4 ms, System: 56.7 ms] Range (min … max): 256.7 ms … 258.1 ms 11 runs Benchmark 2: ./git -C ../linux ls-files --format='%(objectsize)' Time (mean ± σ): 253.4 ms ± 0.3 ms [User: 193.6 ms, System: 56.6 ms] Range (min … max): 253.0 ms … 253.8 ms 11 runs Benchmark 3: ./git_main -C ../linux ls-files --format='%(objectsize:padded)' Time (mean ± σ): 257.9 ms ± 0.3 ms [User: 198.0 ms, System: 56.6 ms] Range (min … max): 257.3 ms … 258.5 ms 11 runs Benchmark 4: ./git -C ../linux ls-files --format='%(objectsize:padded)' Time (mean ± σ): 254.6 ms ± 1.0 ms [User: 194.6 ms, System: 56.7 ms] Range (min … max): 253.7 ms … 256.8 ms 11 runs Summary ./git -C ../linux ls-files --format='%(objectsize)' ran 1.00 ± 0.00 times faster than ./git -C ../linux ls-files --format='%(objectsize:padded)' 1.02 ± 0.00 times faster than ./git_main -C ../linux ls-files --format='%(objectsize)' 1.02 ± 0.00 times faster than ./git_main -C ../linux ls-files --format='%(objectsize:padded)' Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13cat-file: use strbuf_add_uint()René Scharfe1-2/+2
Speed up printing of objectsize atoms by using the specialized function strbuf_add_uint() instead of the general-purpose function strbuf_addf(): Benchmark 1: ./git_main cat-file --batch-all-objects --batch-check='%(objectsize)' Time (mean ± σ): 751.7 ms ± 1.5 ms [User: 733.5 ms, System: 17.1 ms] Range (min … max): 750.5 ms … 755.0 ms 10 runs Benchmark 2: ./git cat-file --batch-all-objects --batch-check='%(objectsize)' Time (mean ± σ): 720.4 ms ± 0.4 ms [User: 701.9 ms, System: 16.7 ms] Range (min … max): 719.7 ms … 721.2 ms 10 runs Summary ./git cat-file --batch-all-objects --batch-check='%(objectsize)' ran 1.04 ± 0.00 times faster than ./git_main cat-file --batch-all-objects --batch-check='%(objectsize)' Benchmark 1: ./git_main cat-file --batch-all-objects --batch-check='%(objectsize:disk)' Time (mean ± σ): 404.6 ms ± 0.9 ms [User: 397.8 ms, System: 5.7 ms] Range (min … max): 403.3 ms … 405.9 ms 10 runs Benchmark 2: ./git cat-file --batch-all-objects --batch-check='%(objectsize:disk)' Time (mean ± σ): 378.3 ms ± 0.9 ms [User: 371.2 ms, System: 5.9 ms] Range (min … max): 376.8 ms … 380.2 ms 10 runs Summary ./git cat-file --batch-all-objects --batch-check='%(objectsize:disk)' ran 1.07 ± 0.00 times faster than ./git_main cat-file --batch-all-objects --batch-check='%(objectsize:disk)' Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13strbuf: add strbuf_add_uint()René Scharfe2-0/+18
strbuf_addf() calls vsnprintf(3) underneath, which supports a plethora of formatting options. We can avoid its overhead in basic cases by providing specialized functions like strbuf_addstr() for strings. Add another one, strbuf_add_uint(), for unsigned integers. Prepare the number string in a temporary buffer. Make it big enough for any unsigned integer value: A decimal digit can represent ln(10)/ln(2) ≈ 3.32 bits; dividing the number of bits of uintmax_t by 3.3 and rounding up gives a sufficiently close conservative size estimate. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-13merge: use repo_in_merge_bases for octopus up-to-date checkKristofer Karlsson2-14/+14
The octopus merge path checks whether each remote head is already an ancestor of HEAD by computing all merge-bases via repo_get_merge_bases() and comparing the first result's OID to the remote head. This is more expensive than necessary: repo_get_merge_bases() calls paint_down_to_common() with min_generation=0, performs the full STALE drain, and may run remove_redundant(), when all we need is a yes/no reachability answer. Replace this with repo_in_merge_bases(), which answers the is-ancestor question directly. When generation numbers are available, repo_in_merge_bases() uses can_all_from_reach() -- a DFS bounded by generation number that stops as soon as the target is found or ruled out, without entering paint_down_to_common() at all. Without generation numbers, it still benefits from a tighter min_generation floor. Signed-off-by: Kristofer Karlsson <krka@spotify.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-12The second batchJunio C Hamano1-0/+21
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-12Merge branch 'js/maintenance-fix-deadlock-on-win10'Junio C Hamano3-3/+67
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
2026-05-12Merge branch 'jc/t5551-fix-expensive'Junio C Hamano1-0/+1
Test fix. * jc/t5551-fix-expensive: t5551: "GIT_TEST_LONG=Yes make test" is broken
2026-05-12Merge branch 'js/t5564-socks-use-short-path'Junio C Hamano1-2/+9
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