Junio C Hamano [Thu, 30 Jul 2026 17:40:48 +0000 (10:40 -0700)]
Merge branch 'hn/checkout-m-autostash-refine' into jch
The autostash fallback in 'git checkout -m' has been refined to only
retry when there are local changes. Additionally, a blank line now
visually separates autostash conflict advice from the subsequent
branch-switch message.
Junio C Hamano [Thu, 30 Jul 2026 17:40:48 +0000 (10:40 -0700)]
Merge branch 'ns/merge-base-is-ancestor-tests' into jch
Tests for 'git merge-base --is-ancestor' have been added to cover
exit codes (0 for success, 1 for non-ancestor, 128 for errors) and
to ensure it cannot be combined with '--all'.
* ns/merge-base-is-ancestor-tests:
merge-base: add tests for --is-ancestor
Junio C Hamano [Thu, 30 Jul 2026 17:40:47 +0000 (10:40 -0700)]
Merge branch 'cl/regexec-macos-leak' into jch
A compatibility workaround has been introduced for macOS to address
a memory leak in the system regex engine when it encounters invalid
multibyte sequences. The workaround segments the input buffer at
invalid byte boundaries and searches each valid segment separately
using regexec(), avoiding the leaking path.
* cl/regexec-macos-leak:
SQUASH???
regexec: work around macOS TRE leak on invalid UTF-8
Junio C Hamano [Thu, 30 Jul 2026 17:40:47 +0000 (10:40 -0700)]
Merge branch 'tc/replay-linearize' into jch
The 'git replay' command has been taught the '--linearize' option to
drop merge commits and linearize the replayed history, mimicking 'git
rebase --no-rebase-merges'.
* tc/replay-linearize:
replay: offer an option to linearize the commit topology
replay: resolve the replay base outside pick_regular_commit()
replay: add helper to put entry into replayed_commits
Junio C Hamano [Thu, 30 Jul 2026 17:40:47 +0000 (10:40 -0700)]
Merge branch 'lo/mv-missing-dest-dir-check' into jch
'git mv' has been updated to check for a missing destination
leading directory during the checking phase, allowing 'git mv -n'
to report the failure. The error message when the rename(2)
syscall fails has also been improved to name both the source and
the destination.
* lo/mv-missing-dest-dir-check:
mv: reject a destination whose leading path is missing or a symlink
mv: name both source and destination when rename fails
Junio C Hamano [Thu, 30 Jul 2026 17:40:46 +0000 (10:40 -0700)]
Merge branch 'js/coverity-unchecked-returns-fix' into jch
A handful of code paths have been corrected to check return values
from functions like curl_easy_duphandle(), deflateInit(), lseek(),
dup(), and strbuf_getline_lf(), resolving several Coverity warnings
about unchecked returns.
* js/coverity-unchecked-returns-fix:
bisect: handle dup() failure when redirecting stdout
bisect: check get_terms return at all call sites
bisect: check strbuf_getline_lf return when reading terms
transport-helper: warn when export-marks file cannot be finalized
transport-helper: check dup() return in get_exporter
compat/pread: check initial lseek for errors
last-modified: handle repo_parse_commit() failures
reftable tests: check reftable_table_init_ref_iterator() return
reftable/block: check deflateInit() return value
config: propagate launch_editor() failure in show_editor()
http: die on curl_easy_duphandle failure in get_active_slot
Junio C Hamano [Thu, 30 Jul 2026 17:40:46 +0000 (10:40 -0700)]
Merge branch 'mm/lib-httpd-cgi-safe' into jch
CGI helper scripts used by HTTP-related test scripts have been updated
to use atomic filesystem operations, preventing race conditions when
Apache handles concurrent requests.
* mm/lib-httpd-cgi-safe:
t/README: document writing concurrency-safe helpers
t/lib-httpd: make http-429 first-request check atomic
t/lib-httpd: fix apply-one-time-script race under concurrent requests
Junio C Hamano [Thu, 30 Jul 2026 17:40:46 +0000 (10:40 -0700)]
Merge branch 'ij/subtree-reject-v2-config' into jch
The shell script implementation of 'git subtree' has been updated to
check for the presence of the configuration file of the new Rust
implementation, preventing users from accidentally running the old
script on repositories already managed by the new tool.
* ij/subtree-reject-v2-config:
git-subtree: Bail out if we find output from Rust rewrite (test)
git-subtree: Bail out if we find output from Rust rewrite
Junio C Hamano [Thu, 30 Jul 2026 17:40:46 +0000 (10:40 -0700)]
Merge branch 'ds/sparse-index-ita-crash' into jch
A crash in the 'sparse-index' collapse code when encountering an
invalidated cache-tree node (due to an intent-to-add path) has been
fixed by avoiding collapsing such subtrees.
* ds/sparse-index-ita-crash:
sparse-index: avoid crash on intent-to-add entry outside the cone
Junio C Hamano [Thu, 30 Jul 2026 17:40:46 +0000 (10:40 -0700)]
Merge branch 'kh/doc-trailers' into jch
Documentation for 'git interpret-trailers' has been updated to explain
the format of trailer keys (alphanumeric characters and hyphens),
replace outdated terminology, define key terms upfront, and document
how comment lines in the input are treated.
* kh/doc-trailers:
doc: interpret-trailers: document comment line treatment
doc: interpret-trailers: rewrite new-trailers paragraphs
doc: interpret-trailers: commit to “trailer block” term
doc: interpret-trailers: join new-trailers again
doc: interpret-trailers: add key format example
doc: interpret-trailers: explain key format
doc: interpret-trailers: explain the format after the intro
doc: interpret-trailers: not just for commit messages
doc: interpret-trailers: use “metadata” in Name as well
doc: interpret-trailers: replace “lines” with “metadata”
doc: interpret-trailers: stop fixating on RFC 822
Junio C Hamano [Thu, 30 Jul 2026 17:40:45 +0000 (10:40 -0700)]
Merge branch 'za/completion-hide-dotfiles' into jch
Path completion for commands like 'git rm' and 'git mv' has been
updated to hide dotfiles by default unless the user explicitly starts
the path with a dot, matching standard shell-completion behavior.
* za/completion-hide-dotfiles:
completion: hide dotfiles by default for path completion
completion: hide dotfiles for selected path completion
Junio C Hamano [Thu, 30 Jul 2026 17:40:45 +0000 (10:40 -0700)]
Merge branch 'kh/doc-replay-config' into jch
Documentation for 'git replay' has been updated to refer to its
configuration variables.
* kh/doc-replay-config:
doc: replay: move “default” to the right-hand side
doc: replay: use a nested description list
doc: replay: improve config description
doc: link to config for git-replay(1)
Junio C Hamano [Thu, 30 Jul 2026 17:40:45 +0000 (10:40 -0700)]
Merge branch 'bl/t7412-use-test-path-helpers' into jch
The test script 't7412' that tests 'git submodule absorbgitdirs' has
been modernized to use test_path_is_file(), test_path_is_dir(), and
test_path_is_missing() helper functions instead of raw 'test -[fde]'
commands.
* bl/t7412-use-test-path-helpers:
submodule absorbgitdirs tests: use test_* helper functions
Junio C Hamano [Thu, 30 Jul 2026 17:40:45 +0000 (10:40 -0700)]
Merge branch 'jm/t0213-skip-emulated-ancestry-tests' into jch
The 'TRACE2_ANCESTRY' prerequisite in the 't0213' test script has been
refined to avoid failures under user-mode emulation by verifying that
the ancestry collector reports the expected process names rather than
the emulator binary name.
* jm/t0213-skip-emulated-ancestry-tests:
t0213: skip ancestry tests under user-mode emulation
Junio C Hamano [Thu, 30 Jul 2026 17:40:44 +0000 (10:40 -0700)]
Merge branch 'jc/add-resolved' into jch
'git add' has been taught a new '--resolved' option to stage
conflict-resolved paths, while leaving unrelated local changes
unstaged. It scans the unmerged paths for leftover conflict markers
and aborts if any are found.
Junio C Hamano [Thu, 30 Jul 2026 17:40:44 +0000 (10:40 -0700)]
Merge branch 'dl/pack-bitmap-position-zero' into jch
A boundary case check in reachability bitmap traversal has been
corrected to properly handle the object at position zero, which was
previously skipped, leading to redundant bitmap loading.
* dl/pack-bitmap-position-zero:
pack-bitmap: handle objects at bitmap position zero
Junio C Hamano [Thu, 30 Jul 2026 17:40:43 +0000 (10:40 -0700)]
Merge branch 'mm/revision-pure-get-commit-action' into jch
The get_commit_action() function has been refactored to be a pure
predicate by moving the side-effecting line-level log range folding to
simplify_commit(). This ensures that evaluating a commit's action
before the walk reaches it does not prematurely mutate its tracked
line ranges, making it safer for potential lookahead evaluations.
* mm/revision-pure-get-commit-action:
revision: make get_commit_action() a pure predicate
Junio C Hamano [Thu, 30 Jul 2026 17:40:43 +0000 (10:40 -0700)]
Merge branch 'js/mingw-symlink-net-share-leak' into jch
Git for Windows has been updated to avoid auto-detecting the symlink
type if the target path starts with a slash, preventing NTLM
credential leaks when checking out repositories with crafted
symbolic links pointing to network shares.
* js/mingw-symlink-net-share-leak:
mingw: skip symlink type auto-detection for network share targets
Junio C Hamano [Thu, 30 Jul 2026 17:40:42 +0000 (10:40 -0700)]
Merge branch 'jk/t0014-dynamic-deprecated-cmds' into jch
The alias tests in t/t0014-alias.sh have been updated to dynamically
query the list of deprecated commands using 'git
--list-cmds=deprecated' to avoid test failures when running with
WITH_BREAKING_CHANGES in a build directory that contains stale
executables of formerly deprecated commands.
* jk/t0014-dynamic-deprecated-cmds:
t0014: generate deprecated command names dynamically
t0014: factor out choice of deprecated commands
Junio C Hamano [Thu, 30 Jul 2026 17:40:42 +0000 (10:40 -0700)]
Merge branch 'jk/ci-static-analysis-image-bump' into jch
The image version used by the static-analysis CI job has been bumped
to ubuntu-latest (Ubuntu 24.04), which brings in a newer Coccinelle
version that resolves a severe performance regression. A false
positive warning from the CHECK_ASSERTION_SIDE_EFFECTS build with
GCC 15 in the Bloom filter code has also been silenced to facilitate
the image upgrade.
* jk/ci-static-analysis-image-bump:
ci: bump ubuntu image version for static-analysis job
bloom: silence CHECK_ASSERTION_SIDE_EFFECTS false positive
Junio C Hamano [Thu, 30 Jul 2026 17:40:42 +0000 (10:40 -0700)]
Merge branch 'ps/odb-pluggable-housekeeping' into jch
Object database housekeeping in 'git gc' and 'git maintenance' has
been refactored to be pluggable. The files-backend-specific logic,
including incremental and geometric repacking as well as object
pruning, has been moved out of the command implementation and into the
files object database source, enabling future alternative object
database backends to implement their own housekeeping services.
* ps/odb-pluggable-housekeeping:
odb: make optimizations pluggable
builtin/gc: fix signedness issues in ODB-related functionality
builtin/gc: refactor ODB optimizations to operate on "files" source
builtin/gc: introduce `odb_optimize_required()`
builtin/gc: move geometric repacking into `odb_optimize()`
builtin/gc: introduce object database optimization options
builtin/gc: inline config values specific to the "files" backend
builtin/gc: make repack arguments self-contained
builtin/gc: extract object database optimizations into separate function
builtin/gc: move worktree and rerere tasks before object optimizations
odb: run "pre-auto-gc" hook for all maintenance tasks
t7900: simplify how we check for maintenance tasks
Junio C Hamano [Thu, 30 Jul 2026 17:40:41 +0000 (10:40 -0700)]
Merge branch 'pw/rebase-fixup-fixes' into jch
Two bugs in how 'git rebase' handles skipped 'fixup' and 'squash'
commands have been fixed. One bug caused an incorrect commit count to
be shown in the template message when multiple commands were skipped,
and another prevented the editor from opening when the final command
in a chain containing 'fixup -c' was skipped.
* pw/rebase-fixup-fixes:
rebase: remember fixup -c after skipping fixup/squash
rebase -i: fix counting of fixups after rebase --skip
Junio C Hamano [Thu, 30 Jul 2026 17:40:41 +0000 (10:40 -0700)]
Merge branch 'en/submodule-insteadof-remote-match' into jch
The remote-matching logic for submodules has been corrected to
resolve 'url.*.insteadOf' aliases before comparing the inventoried
URL from '.gitmodules' with the URLs of configured remotes.
* en/submodule-insteadof-remote-match:
submodule: resolve insteadOf aliases when matching remote
Junio C Hamano [Thu, 30 Jul 2026 17:40:40 +0000 (10:40 -0700)]
Merge branch 'jc/exclude-first-parent-seen' into jch
Traversals with '--exclude-first-parent-only' have been corrected to
properly stop after the first parent even when it has already been
marked as SEEN.
* jc/exclude-first-parent-seen:
revision: honor --exclude-first-parent-only with SEEN first parent
Junio C Hamano [Thu, 30 Jul 2026 17:40:40 +0000 (10:40 -0700)]
Merge branch 'hn/url-push-tracking' into jch
When the push remote is specified as a URL, the fetch refspec of a
uniquely matching configured remote is now used to find and update
the remote-tracking branch (e.g., '@{push}').
* hn/url-push-tracking:
remote: find tracking branches for URL push destinations
remote: pass repository to push tracking helper
Junio C Hamano [Thu, 30 Jul 2026 17:32:04 +0000 (10:32 -0700)]
Merge branch 'ps/odb-move-loose-object-writing'
The logic to write loose objects has been refactored and moved from
'object-file.c' to the loose backend source file 'odb/source-loose.c',
making the loose backend more self-contained. This is achieved by
first refactoring force_object_loose() to use generic ODB write
interfaces instead of loose-backend internals.
* ps/odb-move-loose-object-writing:
object-file: move logic to write loose objects
object-file: move `force_object_loose()`
object-file: force objects loose via generic interface
object-file: fix memory leak in `force_object_loose()`
odb: support setting mtime when writing objects
odb: lift object existence check out of the "loose" backend
odb: compute object hash in `odb_write_object_ext()`
t/u-odb-inmemory: implement wrapper for writing objects
odb: compute compat object ID in `odb_write_object_ext()`
The object ID shortening and linking in the 'commitdiff' view of
'gitweb' has been corrected to work even when the index line carries
a trailing file mode.
* tl/gitweb-shorten-hashes-with-modes:
gitweb: shorten index hashes with trailing file modes
Junio C Hamano [Thu, 30 Jul 2026 17:32:04 +0000 (10:32 -0700)]
Merge branch 'ps/cat-file-remote-object-info'
The 'remote-object-info' command has been added to 'git cat-file
--batch-command', allowing clients to request object metadata
(currently size) from a remote server via protocol v2 without
downloading the entire object. Format placeholders are dynamically
filtered on the client based on server-advertised capabilities,
returning empty strings for inapplicable or unsupported fields.
* ps/cat-file-remote-object-info:
cat-file: make remote-object-info allow-list adapt to the server
cat-file: add remote-object-info to batch-command
transport: add client support for object-info
serve: advertise object-info feature
protocol-caps: check object existence regardless of the attributes requested
fetch-pack: move fetch initialization
connect: make write_fetch_command_and_capabilities() more generic
fetch-pack: move write_fetch_command_and_capabilities() to connect.c
fetch-pack: use unsigned int for hash_algo variable
fetch-pack: drop the static advertise_sid variable
t1006: extract helper functions into new 'lib-cat-file.sh'
cat-file: declare loop counter inside for()
transport-helper: fix memory leak of helper on disconnect
Junio C Hamano [Thu, 30 Jul 2026 17:32:04 +0000 (10:32 -0700)]
Merge branch 'jt/config-lock-timeout'
Configuration file locking has been updated to retry for a short
period, avoiding failures when multiple processes attempt to update
the configuration simultaneously.
* jt/config-lock-timeout:
config: retry acquiring config.lock, configurable via core.configLockTimeout
The 'git stash push' command has been optimized to avoid unnecessary
sparse index expansion when pathspecs are wholly inside the
sparse-checkout cone. Also, a potential out-of-bounds read in the
sparse-index expansion check helper pathspec_needs_expanded_index()
has been fixed by consistently using the parsed, prefixed path.
* tn/stash-avoid-sparse-index-expansion:
stash: avoid sparse-index expansion for in-cone paths
pathspec: use match for sparse-index expansion checks
Junio C Hamano [Thu, 30 Jul 2026 17:32:03 +0000 (10:32 -0700)]
Merge branch 'sk/userdiff-swift'
Userdiff patterns for Swift have been added, with support for
Swift-specific constructs such as attributes, modifiers, failable
initializers, and generics.
* sk/userdiff-swift:
userdiff: add support for Swift
Junio C Hamano [Thu, 30 Jul 2026 17:32:03 +0000 (10:32 -0700)]
Merge branch 'ty/migrate-excludes-file'
The 'excludes_file' and various other global configuration variables
(including 'editor_program', 'pager_program', 'askpass_program', and
'push_default') have been migrated into the per-repository structure.
* ty/migrate-excludes-file:
repository: adjust the comment of config_values_private_
environment: move object_creation_mode into repo_config_values
environment: move autorebase into repo_config_values
environment: move push_default into repo_config_values
environment: migrate apply_default_whitespace and apply_default_ignorewhitespace
environment: move askpass_program into repo_config_values
environment: move pager_program into repo_config_values
environment: move editor_program into repo_config_values
environment: move excludes_file into repo_config_values
repository: introduce repo_config_values_clear()
Junio C Hamano [Thu, 30 Jul 2026 17:32:02 +0000 (10:32 -0700)]
Merge branch 'ty/migrate-trust-executable-bit'
The 'trust_executable_bit' (coming from the 'core.filemode'
configuration) has been migrated into 'struct repo_config_values' to
tie it to a specific repository instance.
* ty/migrate-trust-executable-bit:
environment: move has_symlinks into repo_config_values
environment: move trust_executable_bit into repo_config_values
read-cache: pass 'repo' to 'ce_mode_from_stat()'
read-cache: remove redundant extern declarations
Junio C Hamano [Thu, 30 Jul 2026 17:32:02 +0000 (10:32 -0700)]
Merge branch 'rs/tempfile-wo-the-repository'
The tempfile and lockfile APIs have been refactored to stop depending
on the 'the_repository' global variable, and their callers have been
updated to use the repository-aware variants.
* rs/tempfile-wo-the-repository:
use repo_hold_lock_file_for_update{,_mode,_timeout}() with custom repos
tempfile: stop using the_repository
lockfile: add repo_hold_lock_file_for_update{,_timeout}{,_mode}()
refs/packed: use repo_create_tempfile()
tempfile: add repo_create_tempfile{,_mode}()
mv: reject a destination whose leading path is missing or a symlink
When moving a file, if any leading directory in the destination path
is missing or is not a real directory, the problem is detected only
later when rename() is called. Furthermore, if a leading directory
component is a symbolic link, the issue is not detected at all.
Three cases reach rename(2) unchecked today:
- A leading directory is missing: rename(2) fails with ENOENT,
reported against the source (misleading), and "git mv -n" does not
detect it since the dry run never reaches the syscall.
- A leading component is a non-directory ("git mv x a/b" with 'a' a
file): rename(2) fails with ENOTDIR, again only at the syscall.
- A leading component is a symbolic link: "git mv" follows it. Since
Git tracks symlinks, the destination is really occupied by a
tracked object, and following it is wrong regardless of the link
target. The move is done on disk at the resolved location while the
index records the literal path, leaving the index describing a
worktree that does not exist. A later "git add" can reconcile it,
but "git mv" alone has already corrupted the state.
Detect all three in the checking phase. Reject a destination that goes
through a symlink with has_symlink_leading_path(), which uses lstat()
and never follows the link, so the refusal is independent of the
target. Then lstat() the leading directory: report "destination
directory does not exist" for ENOENT/ENOTDIR and "destination is not a
directory" for a non-directory. Other errors fall through to rename().
Guard the directory check with the same condition under which rename(2)
runs, so directory moves and sparse/out-of-cone destinations are not
flagged incorrectly.
This changes behavior: a move through a tracked symlink that previously
"succeeded" while corrupting the index is now refused. The other two
cases only change when the failure is diagnosed.
Signed-off-by: Lucas Zamboni Orioli <lucaszam0@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
mv: name both source and destination when rename fails
When "git mv" fails at the rename(2) syscall, the error is reported
with die_errno() using only the source path:
fatal: renaming 'src' failed: No such file or directory
rename(2) returns ENOENT both when the source does not exist and when
a directory component of the destination does not exist, and errno
does not distinguish the two. Reporting only the source therefore
misleads the user in the latter case: for
git mv a/file b/no-such-dir/file
the message blames 'a/file', which exists, and gives no hint that
'b/no-such-dir/' is the missing part.
Inspecting the paths again after the failure to determine which one is
at fault would be racy, since either could appear or disappear between
the rename(2) and the follow-up check. Instead, simply name both the
source and the destination in the message and let the reader see which
one is wrong:
fatal: renaming 'a/file' to 'b/no-such-dir/file' failed:
No such file or directory
Signed-off-by: Lucas Zamboni Orioli <lucaszam0@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: replay: move “default” to the right-hand side
This is now a description list (see previous commit) and parentheticals
like this do not go on the left-hand side. Moving it to the other side
makes it stand out just as much and is also more consistent with the
rest of the documentation.
Let’s also do the same for the `replay.refAction` description list.
That makes the two desc. lists identical in the first sentence. Let’s
add a comment about that for future editors.
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This bullet list for `--ref-action` introduces a term with a colon.
This is exactly what a description list is, structurally. Let’s be
stylistically consistent and use the desc. list markup construct.
In short, just transform this unordered list in the same way that we
did for `replay.refAction` in the previous commit.
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
First of all, this unordered list for `replay.refAction` introduces
a term with a colon. This is exactly what a description list is,
structurally. Let’s be stylistically consistent and use the desc.
list markup construct. Let’s also drop the harmless but unneeded
indentation.
We can reuse the `::` delimiter since we use an open block.
But for consistency use the typical nested description list
delimiter, namely `;;`.
Second, let’s replace the inline-verbatim `git replay` with a link
to git-replay(1), since we are naming the command. But make that
conditional so that we avoid a self-link inside git-replay(1).[1]
† 1: See e.g. e7b3a768 (doc: git-init: rework config item
init.templateDir, 2024-03-10) for another example of
avoiding self-linking
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This config doc was added in 336ac90c (replay: add replay.refAction
config option, 2025-11-06) but never included anywhere. Include it in
git-replay(1) and git-config(1).
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: interpret-trailers: document comment line treatment
Comment lines have always been ignored but this is not documented.
The primary motivation here is to reasonably complete in the
documentation of how trailers are parsed; this is after all the only
documentation page that documents this format. However, and going beyond
that point, we could imagine that someone would want to use this format
outside a commit (or tag) message context, like say in Git notes.
On the other hand, it seems far-fetched that someone would be caught
off guard by this considering that comment characters/strings are not
likely to be alphanumeric,[1] which would mean that these comment lines
would be treated as non-trailer lines if they were *not* detected and
removed as comment lines.
† 1: A notable exception is that Jujutsu VCS uses `JJ:` as
the comment string
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Two commits ago we moved new-trailers paragraph next to each other.
But there is something curious about two of them:
By default the new trailer will appear at the end of the trailer
block. [...]
Then a source block and a paragraph later:
By default, a `<key>=<value>` or `<key>:<value>` argument given
using `--trailer` will be appended after the existing trailers only
if [...]
Why are there two paragraphs that talk about how “By default” a trailer
will be appended?
We can make these paragraphs flow better, and with a more distinct
character each, by dividing the flow like this:
1. Declare that we are about to talk about `--trailer` appending
2. Explain the default behavior
3. Explain how this affects the trailer block
4. Then discuss what each trailer line will look like
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: interpret-trailers: commit to “trailer block” term
We chose to introduce the term “trailer block” into the documentation a
few commits ago.[1] It is used in the code though, so it is not a newly
invented term.
That term was useful to explain where the trailers are found (they
*trail* the message). But it is also useful here, where we explain
how trailers are added to existing messages, how trailer blocks are
found (beyond the simple case in the introduction), and how the end
of the message is found.
Also note that we simplify the “blank line” point. The text says:
A blank line will be added before the new trailer if there isn't one
already.
But this isn’t quite coherent. The previous sentence says “If there is
no existing trailer”, so we are in one of these modes:
1. discussing trailer blocks in general; or
2. discussing creating a new trailer block in particular.
If (1), then we shouldn’t add a blank line before the new trailer if
there exists a trailer block already. And if (2), then the “if there
isn’t one already” is redundant.[2] So just talking about the higher-
level “trailer block” simplifies the text, since we don’t have to worry
about the different contexts that *trailers* can find themselves in.
† 1: in commit “explain the format after the intro”
† 2: Note that non-trailer lines don’t matter here; if you have a
trailer block consisting of `(cherry picked from commit <commit>)`,
then you still shouldn’t insert a blank line before the new trailer
since that would create a new trailer block
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
There are three paragraphs that talk about how a new trailer is added.
But the first one is separated from the other two by two paragraphs
about how `key-alias` can make using `--trailer` more convenient. This
short how-to does not follow thematically from the previous paragraph,
and can wait until we have fully described how a new trailer is
added. So let’s move the three paragraphs about the new-trailer topic
together and move the how-to paragraphs after that.
***
Let’s now review the history of the document. Even if the document
is not quite correct in its current state, just doing the apparently
obvious edit without considering the history does not respect the
effort that went into changing the document in the past.
These three paragraphs were originally next to each other, in the first
version of the doc.[1] But extra sentences about this how-to topic was
added to the first paragraph nine years later:[2]
[...]
`': '` (one colon followed by one space). For convenience, the
<token> can be a shortened string key (e.g., "sign") instead of the
full string which should [...]
And then it was split into it’s own paragraph a little later.[3]
This evolution shows, in my opinion, that this how-to never followed
thematically from the existing topic. Which means that there is nothing
that was potentially lost to time that we need to restore or respect.
† 1: dfd66ddf (Documentation: add documentation for 'git
interpret-trailers', 2014-10-13)
† 2: eda2c44c (doc: trailer: mention 'key' in DESCRIPTION, 2023-06-15)
† 3: 6ccbc667 (trailer doc: <token> is a <key> or <keyAlias>, not both,
2023-09-07)
Suggested-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
All of the examples speak of the Happy Path where everything works
as intended. But failure examples can also be instructive. Especially
for explaining again, by example, the key format (see previous commit).
This also allows us to demonstrate trailer block detection with a
concrete example.
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
A trailer key must consist of ASCII alphanumeric characters and
hyphens *only*. Let’s document it explicitly instead of relying on
readers being conservative and only basing their trailer keys on the
documentation examples.[1]
The previous commit provided us with an appropriate paragraph to
describe the key format.
† 1: Technically they would then miss out on using digits in them since
all of the example keys just use letters and hyphens
Reported-by: Brendan Jackman <jackmanb@google.com> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: interpret-trailers: explain the format after the intro
You need to read the entire “Description” section in order to understand
the full trailer format. But there are many nuances, so that’s fine.
As a starter though we have an introductory example.[1] That turns out
to be crucial; the rest of this section talks about the mechanics of the
command and only incidentally the format itself.
Now, although the example might arguably be self-explanatory, we can
add a little preamble which defines the format in its simplest form as
well as define the most important terms.
Note that we name the “blank line” rule since I want to use that term
every time it comes up. It gets very mildly obfuscated if you call it
a “blank line” in one place[2] and “empty (or whitespace-only) ...” in
another one.[3]
We will define the format of the *key* in the next commit.
† 1: from d57fa7fc (doc: trailer: add more examples in DESCRIPTION,
2023-06-15)
† 2: `Documentation/git-interpret-trailers.adoc:86` in 5361983c (The 22nd batch, 2026-03-27)
† 3: `Documentation/git-interpret-trailers.adoc:93` in 5361983c (The 22nd batch, 2026-03-27)
Suggested-by: D. Ben Knoble <ben.knoble+github@gmail.com> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: interpret-trailers: not just for commit messages
This command doesn’t interface with commits directly. You can
interpret or modify any kind of text, even though commit messages
are the most relevant.
The git(1) suite also isn’t restricted to only direct commit support
since git-tag(1) learned `--trailer` in 066cef77 (builtin/tag: add
--trailer option, 2024-05-05)
Now, we already introduce the command in the “Name” section as dealing
with commit messages as well. That is fine since that intro line needs
to remain pretty short.
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: interpret-trailers: use “metadata” in Name as well
We now since the previous commit introduce the format as “trailer
metadata”. We can replace “structured information” with “metadata”
in the “Name” section to be consistent.
While “structured information” does emphasize that the data is not
loosely structured, we also say that this command adds to or parses
this format. I don’t think that we need to emphasize that it is
structured since clearly there is some structure there.
Both “metadata” and “structured information” can convey the same
information. But “metadata” is shorter and easier to deploy since
it’s just one word.
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: interpret-trailers: replace “lines” with “metadata”
We removed the initial comparison to email headers in the previous
commit. Now the introduction paragraph just says “trailer lines”, and
the only hint that this is metadata/structured information is the
“otherwise free-form” phrase.
Let’s replace “lines” with “metadata” since that is their purpose.
This also makes the introduction more consistent with how I chose
to define trailers in the glossary:[1] “Key-value metadata”. (We will
introduce “key–value” in the upcoming commit “explain the format after
the intro”.)
Let’s not emphasize “trailer” here since we are going to define the term
in the upcoming commit “explain the format after the intro”.
Let’s call it “trailer metadata” rather than “trailers metadata”.
At first it seemed better to use the latter:
1. We’re introducing the jargon, and the format is often discussed as
plural “trailers”, with its constituent parts being singular
“trailer”
2. What this replaces uses “trailer”, but it rescues the plural mood
with “lines”
3. This is very soon going to go into the constituent parts, including
each trailer, so we’re contrasting the concept name (trailers) with
its parts
But:
1. The former reads better (most important)
2. “Trailer *metadata*” suggests plurality, similar to “trailer *lines*”
Helped-by: Matt Hunter <m@lfurio.us> Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This command handles the trailer metadata format. But the command
isn’t introduced as such; it is instead introduced by stating that
these trailer lines look similar to RFC 822 email headers.
This is overwrought; most people do not deal directly with email
headers, and certainly not email RFCs.
Trailers are just key–value pairs that, like email headers, use colon
as the separator. The format in its simplest form is easy to describe
directly without comparing it to anything else; we will do that in the
upcoming commit “explain the format after the intro”.
For now, let’s:
• remove the first mention of email headers;
• keep the second, innocuous comparison with email line folding in the
middle; and
• remove the now-unneeded disclaimer that trailers do not share many of
the features of RFC 822 email headers—there is no invitation to
speculate that trailers would follow any other email format rules
since we do not compare them directly any more.
***
Talking about trailers as an RFC 822/2822-like format seems to go back
to the `--fixes`/`Fixes:` trailer topic,[1] the thread that precipitated
this command and in turn the first trailer support in git(1) beyond
adding s-o-b lines.
`git merge-base --is-ancestor A B` is used a lot in scripts but has no
tests. Add some to t6010 covering its exit codes: 0 when A is an
ancestor of B, 1 when it is not, and 128 (not 1) when given a bad
argument. Also check that --is-ancestor and --all can't be combined,
and that the resulting error names both options.
Signed-off-by: Nikolaus Schuetz <nikolauspschuetz@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano [Wed, 29 Jul 2026 17:25:24 +0000 (10:25 -0700)]
add: introduce '--resolved' option
During a conflicted merge, rebase, or cherry-pick, 'git add -u' is a
handy way to add modified paths to the index. However, '-u'
indiscriminately adds all modified tracked paths, including unmerged
paths that may still contain unresolved conflict markers. It also
adds tracked files modified in the worktree that are not involved in
the ongoing merge.
The latter is not a huge problem for "git rebase", which refuses to
start with any local changes, but is a problem for "git merge",
which is often run with local changes in maintainer workflows.
Introduce 'git add --resolved' to add only unmerged paths, limited
by an optional pathspec, where no conflict markers remain in the
working tree.
Before modifying the index, scan unmerged regular files for leftover
conflict markers using a new helper, has_conflict_markers(), defined
in merge-ll.c in terms of the is_conflict_marker_line() helper we
introduced earlier. If any unmerged path still contains conflict
markers, show an error listing the conflicted paths and abort
without updating the index. Otherwise, add these unmerged paths
that do not have conflict markers to the index.
Note that unmerged paths without conflict markers (such as binary
files and deletions) are added as resolved using add_file_to_index()
and remove_file_from_index_with_flags(). Tracked files that were
not in a conflicted state are ignored by '--resolved'.
add_file_to_index() takes flags such as ADD_CACHE_PRETEND and
ADD_CACHE_VERBOSE and internally handles both reporting (e.g.,
"add 'path'") and suppressing index updates during dry runs.
In contrast, remove_file_from_index() takes only istate and path
without flags. Callers that perform file removals (such as
update_callback() in read-cache.c) are forced to manually inspect
ADD_CACHE_PRETEND and ADD_CACHE_VERBOSE flags for removed
files.
Introduce remove_file_from_index_with_flags() to encapsulate
pretend mode and verbose reporting for index removals. Update
update_callback() to use the new helper.
The diff.c:is_conflict_marker() and rerere.c:is_cmarker() functions
implement duplicate logic for identifying conflict marker lines
(lines that begin with a run of '<', '=', '>', and '|' characters).
diff.c's original version from 049540435f (diff --check: detect
leftover conflict markers, 2008-06-26) accepts any whitespace (such
as a newline) immediately following '<<<<<<<' and '>>>>>>>', whereas
rerere.c's version from 191f241717 (rerere: prepare for customizable
conflict marker length, 2010-01-16) strictly requires a space
character (' ') after them.
Implement is_conflict_marker_line() in merge-ll.c to serve as a
replacement for both, and update diff.c and rerere.c to use the new
helper. The unified helper intentionally adopts rerere's stricter
rule, as the conflicts generated by Git always show the "ours" and
"theirs" labels after these markers separated by a space.
Junio C Hamano [Wed, 29 Jul 2026 17:25:21 +0000 (10:25 -0700)]
read-cache: reindent
I do not know how this happened without anybody noticing, but a few
months ago we added a16c4a245a (read-cache: submodule add need
--force given ignore=all configuration, 2026-02-06), and almost all
lines the patch added were incorrectly indented.
Reindent these lines so that they play better with surrounding lines
in the same file.
We have a few tests related to aliasing of deprecated commands. They use
whatchanged and pack-redundant because those are the only two deprecated
commands we have. Eventually those commands will be removed, at which
point these tests will be checking nothing useful (they'll just be
regular aliases, which we already cover in other tests).
We could remove them at that point, but the code to handle deprecated
commands will still remain. We probably do want to keep the tests around
for the eventual day that we deprecate more commands. So let's ask Git
for its list of deprecated commands, and if we don't have any, skip
those tests.
This also prevents an annoying corner case when your build directory
contains old build products. Right now those commands are marked as
deprecated builtins and treated specially; we allow aliases and never
look for them as dashed external commands. But after they are removed,
they aren't special anymore. If your directory happens to contain
hardlinks from the build of an older version, that confuses Git: it sees
the old hardlinks in place, thinks those are actual external commands,
and refuses to allow aliasing.
You can see that today like this:
make
make WITH_BREAKING_CHANGES=1 test
The first "make" creates git-whatchanged as a hardlink to Git, and the
second does not clean it up (it doesn't know about the whatchanged
command at all anymore). t0014 fails because Git won't create an alias
to the "external" whatchanged command.
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Tue, 28 Jul 2026 14:37:26 +0000 (10:37 -0400)]
t0014: factor out choice of deprecated commands
We have a few tests related to aliasing deprecated commands which use
"whatchanged" and "pack-redundant", as these are the only two deprecated
commands we have. Let's pull those names into variables so that we can
refactor the tests without relying on the specific names.
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
mingw: skip symlink type auto-detection for network share targets
On Windows, symbolic links come in two flavors: file symlinks and
directory symlinks. Since Git was born on Linux where this distinction
does not exist, Git for Windows has to auto-detect the type by looking
at the target. When the target does not yet exist at symlink creation
time, Git for Windows creates a "phantom" file symlink and later, once
checkout is complete, calls `CreateFileW()` on the target to check
whether it is actually a directory.
If the symlink target is a UNC path (e.g. `\\attacker\share`), this
auto-detection triggers an SMB connection to the remote host. Windows
performs NTLM authentication by default for such connections, which
means a crafted repository can exfiltrate the cloning user's NTLMv2 hash
to an attacker-controlled server without any user interaction beyond
`git clone -c core.symlinks=true <url>`.
There are ways to specify UNC paths that start with only a single
backslash (e.g. `\??\UNC\host\share`); All of them do start like that,
though, so let's use that as a tell-tale that we should skip the
auto-detection in `process_phantom_symlink()`. The symlink is then left
as a file symlink (the `mklink` default), and a warning is emitted
suggesting the user set the `symlink` gitattribute to `dir` if a
directory symlink is needed. When the attribute is already set,
auto-detection is never invoked in the first place, so that code path is
unaffected.
This is the same class of vulnerability as CVE-2025-66413
(https://github.com/git-for-windows/git/security/advisories/GHSA-hv9c-4jm9-jh3x)
and follows the same general mitigation pattern that MinTTY adopted for
ANSI escape sequences referencing network share paths
(https://github.com/mintty/mintty/security/advisories/GHSA-jf4m-m6rv-p6c5).
Note that there are legitimate paths starting with a single backslash
that are _not_ network paths: drive-less absolute paths are interpreted
as relative to the current working directory's drive. In practice, these
are highly uncommon (and brittle, just one working directory change away
from breaking). In any case, the only consequence is now that the
symlink type of those has to be specified via Git attributes, is all.
Reported-by: Justin Lee <jessdhoctor@gmail.com>
Addresses: CVE-2026-32631 Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Chungmin Lee [Tue, 28 Jul 2026 05:25:38 +0000 (22:25 -0700)]
regexec: work around macOS TRE leak on invalid UTF-8
On macOS, the system regex engine leaks an internal buffer when
regexec() encounters an invalid multibyte sequence in a UTF-8 locale.
The line-by-line path can call regexec_buf() for each pattern on every
line, so "git grep" can leak repeatedly on a file containing invalid
UTF-8. The total leak grows with the number of calls, and the per-call
allocation grows with the pattern's automaton. In one case, grepping a
repository containing PDFs exhausted memory and caused the machine to
restart.
ce025ae4f61e (grep: disable lookahead on error, 2024-10-20) made "git
grep" fall back to line-by-line matching when regexec() reports an error
on invalid UTF-8. That fallback cannot prevent this leak: the allocation
has already leaked when regexec() returns REG_ILLSEQ.
Avoid the leaking path by providing a Darwin-specific regexec_buf().
Walk the input with mbrtowc(), split it at bytes that cannot form a
complete multibyte character, and search each valid segment separately.
This preserves matches in valid text on either side of an invalid byte.
Search each segment with REG_STARTEND so match offsets remain relative to
the original buffer. Set REG_NOTBOL and REG_NOTEOL for internal segment
boundaries so "^" and "$" do not match there. Keep the flags clear at
the true beginning and end of the buffer.
Use the normal regexec_buf() path in single-byte locales, where no byte
can form an invalid multibyte sequence. Use the bundled regex
implementation unchanged when NO_REGEX is enabled.
Declare the Darwin override in compat/darwin.h and map regexec_buf() to
darwin_regexec_buf(). This follows the platform override pattern used
by the other compatibility headers and leaves the common inline
implementation as the default.
There is no reliable way to detect a future macOS version in which the
system regex implementation has been fixed. Even after a fix, Git will
need the workaround while it supports affected macOS releases, so treat
it as an indefinite compatibility workaround.
Add tests for matches before, after, and between invalid bytes, including
an offset check after an invalid byte. Also check incomplete trailing
input and anchors at true and internal line boundaries.
Signed-off-by: Chungmin Lee <chungmin@chungminlee.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
David Lin [Tue, 28 Jul 2026 13:52:48 +0000 (09:52 -0400)]
pack-bitmap: handle objects at bitmap position zero
`bitmap_position()` only returns a negative value when an object is not
present in the bitmap index.
In `find_objects()`, we have added a check (11d45a6e6a) to avoid
processing a root whose reachability is already represented by the base
bitmap, but accidentally uses `pos > 0`. Consequently, it never performs
the membership test for an object at position zero.
If that object has an individual reachability bitmap, we unnecessarily
OR that bitmap into the base again. Otherwise, we add the object to the
not-mapped list, only for the subsequent pass to recognize that it is
already present. The latter pass correctly treats all non-negative
positions as valid, so this does not change the resulting object set,
but an off-by-one edge case.
Treat position zero as valid by changing the condition to `pos >= 0`.
The existing pseudo-merge traversal test exercises this case. Its
position-zero commit is presented through multiple roots. Before this
change, each occurrence is counted as a bitmap hit; afterwards, only
the first occurrence is counted. Assert the resulting hit count to
cover the boundary condition.
Also cover the non-pseudo-merge case by passing `HEAD` twice. The first
occurrence initializes the base from its stored bitmap, and the second
must recognize that position zero is already present.
Helped-by: Taylor Blau <ttaylorr@openai.com> Signed-off-by: David Lin <davidlin@stripe.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Harald Nordgren [Sat, 25 Jul 2026 15:34:29 +0000 (15:34 +0000)]
checkout -m: refine autostash fallback
When unpack_trees() fails under "git checkout -m", only create an
autostash and retry if there are tracked local changes. Without such
changes, the fallback cannot help and merely repeats the same failure.
Use the conflict result from apply_autostash_ref() to print a blank line
before the branch-switch message, visually separating it from the
conflict advice.
Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Harald Nordgren [Sat, 25 Jul 2026 15:34:28 +0000 (15:34 +0000)]
sequencer: teach autostash apply to report conflicts
Add a conflicted parameter to apply_save_autostash_oid() and
apply_save_autostash_ref() so callers can learn whether applying the
stash resulted in conflicts. Thread the parameter through
apply_autostash_ref() and update existing callers to pass NULL.
Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
replay: offer an option to linearize the commit topology
One of the stated goals of git-replay(1) is to allow implementing the
git-rebase(1) functionality on the server side.
The default mode of git-rebase(1) is to act as if `--no-rebase-merges`
was given. This mode drops merge commits instead of replaying them, and
linearizes the history into a sequence of regular (single-parent)
commits.
Add option `--linearize` to git-replay(1) to do the same. Each replayed
commit is stacked on top of the previously replayed one. When a merge is
encountered, the commits reachable from all of its sides are replayed
into the single line and the merge itself is dropped.
If a ref was pointing to a merge commit, that ref is updated to the
merge's last replayed ancestor.
git-replay(1) accepts multiple revision ranges, for example:
$ git replay --onto main topic1 topic2
Without `--linearize` this replays 'topic1' and 'topic2' onto 'main'
independently and updates both refs.
For now this is disallowed with option `--linearize`. Linearizing more
than one branch at once would concatenate unrelated histories into a
single line, and update each branch to some point in that line. That
won't be the result most users want, especially because the order
depends on the order of the revision walk, not the order of the branch
names on the command line.
For the same reason disallow the use of `--contained` with
`--linearize`.
Users who want to linearize multiple branches are advised to do this in
separate git-replay(1) invocations. Linearizing multiple branches at
once might be added later.
Note that `--linearize` is not modeled after git-rebase(1)'s
`--rebase-merges[=<mode>]` interface. Recreating merges, by preserving
their topology, is a distinct operation that would be a separate mode.
`--linearize` only drops merges and replays commits linearly. So
git-replay(1) uses its own option rather than reusing that interface.
Based-on-patches-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
replay: resolve the replay base outside pick_regular_commit()
Depending on what gets passed into the function pick_regular_commit(),
it decides the new base for the replayed commit. It first tries to find
the replayed results of `pickme`'s parent in the `replayed_commits` map.
If not found, it falls back to `onto`.
When using git-replay(1) with --onto, the fallback is the revision
passed in with this option, but when using --revert, the fallback is
`last_commit`.
It's rather confusing the base is decided partly inside
pick_regular_commit() and partly by its caller.
Move the base selection completely into the caller: replay_revisions().
This bundles all the logic of deciding on the base together. Also, this
reduces the number of parameters of pick_regular_commit(), making its
interface cleaner.
This refactoring doesn't bring any behavior changes.
Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
replay: add helper to put entry into replayed_commits
The function replay_revisions() in replay.c is rather lengthy. Extract
the logic to put a commit entry into a `struct mapped_commits` into a
helper function put_mapped_commit().
While at it, rename mapped_commit() to get_mapped_commit() to pair with
this new function.
Signed-off-by: Toon Claes <toon@iotcl.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
By default the setting 'merge.defaultToUpstream' for git-merge(1) is set
to 'true', which means when `git merge` is invoked with no arguments it
merges the upstream branch configured for the current branch.
With this configuration set to 'true', setup_with_upstream() is called.
That function allocates an array of arguments and hands it back to
cmd_merge() via its `argv` parameter. This array is never freed, so
cmd_merge() leaks it on every invocation.
Track the allocated array in a separate variable and free it at the end.
The leak has been present since 93e535a5b7 (merge: merge with the
default upstream branch without argument, 2011-03-24). Although the leak
sanitizer was enabled for tests in fc1ddf42af (t: remove
TEST_PASSES_SANITIZE_LEAK annotations, 2024-11-21), it went unnoticed
because no test calls `git merge` without arguments, exercising the
default-to-upstream path. Add such a test in t7600, which fails under
the leak sanitizer without this fix.
Signed-off-by: Toon Claes <toon@iotcl.com> Acked-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Tue, 28 Jul 2026 15:00:31 +0000 (11:00 -0400)]
cat-file: handle content request for --batch-command without type
The batch mode of cat-file needs to know the object's type in order to
print the contents (because it decides whether to stream or not based on
object type). The default batch output contains %(objecttype), so we get
the type info automatically. But when it doesn't, we have to ask for it
explicitly.
In the --batch code path, we check while setting up the object_info
struct whether we will print the contents, and if so set "typep" to get
the value. This comes from 6554dfa97a (cat-file: handle --batch format
with missing type/size, 2013-12-12).
But later we added a --batch-command mode, which does not do the same
trick. The decision about whether to retrieve the contents is made
per-command (a "contents" vs "info" command), so we can't decide when
building the object_info originally. As a result, asking for:
will fail the assertion in print_object_or_die() that the type was
actually filled in.
We can fix it by tweaking the object_info on the fly as we receive each
command. But we should be careful to restore it afterwards; otherwise a
sequence of commands like:
contents $one
info $two
info $three
will pay the type-lookup price for $two and $three when it does not need
to. This wouldn't be incorrect, but just slightly inefficient (and hence
there are no tests for that part, because the externally-visible
behavior is the same).
Reported-by: Alan Stokes <alan@source.dev> Helped-by: Pablo Sabater <pabloosabaterr@gmail.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Sun, 26 Jul 2026 08:47:05 +0000 (04:47 -0400)]
diff-lib: skip paths outside prefix in oneway_diff()
Commit 8174627b3d (diff-lib: ignore paths that are outside $cwd if
--relative asked, 2021-08-22) taught run_diff_files() to skip entries
outside the requested prefix before processing them.
Do the same in oneway_diff(), which handles the diff-index code path.
The lower-level diff queue functions already reject such paths, but
checking here avoids unnecessary work and keeps them out of every
do_oneway_diff() code path.
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Sun, 26 Jul 2026 08:46:22 +0000 (04:46 -0400)]
diff-lib: drop stale comment about advancing o->pos
The comment above oneway_diff() claims that the callback must advance
o->pos to skip index entries it has already processed. That stopped
being true in da165f470e (unpack-trees.c: prepare for looking ahead in
the index, 2010-01-07), which moved that bookkeeping into
unpack_trees().
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Tue, 28 Jul 2026 15:14:58 +0000 (11:14 -0400)]
diff-lib: add idx/tree sanity check to oneway_diff
When looking just at the code in oneway_diff(), it seems possible for
both "idx" and "tree" to be NULL, in which case we'd potentially
segfault while checking the relative prefix.
But if you consider what these items actually mean, it shouldn't be
possible for both to be NULL. Let's add an assertion and a comment
documenting this. It might help human readers, but should also silence
static analyzers like Coverity which complain about the potential
segfault.
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano [Mon, 27 Jul 2026 16:09:01 +0000 (09:09 -0700)]
Merge branch 'bc/rust-hash-cleanups'
A few memory problems in the Rust interface to C hash functions have
been corrected. The 'Clone' implementation of 'CryptoHasher' now
properly initializes the context before cloning, and its 'Drop'
implementation now discards the context to prevent leaks.
* bc/rust-hash-cleanups:
rust: discard hash context when finished
hash: initialize context before cloning
Junio C Hamano [Mon, 27 Jul 2026 16:09:00 +0000 (09:09 -0700)]
Merge branch 'pw/rebase-drop-notes-with-commit'
The rebase post-rewrite notes-copying logic has been corrected. When
a commit is dropped during rebase (e.g., because its changes are
already upstream), it is no longer recorded as rewritten, preventing
its notes from being copied to an unrelated commit.
* pw/rebase-drop-notes-with-commit:
sequencer: do not record dropped commits as rewritten
sequencer: use an enum to represent result of picking a commit
sequencer: simplify pick_one_commit()
sequencer: remove unnecessary condition in pick_one_commit()
sequencer: simplify handling of fixup with conflicts
sequencer: remove unnecessary "or" in pick_one_commit()
sequencer: never reschedule on failed commit
sequencer: be more careful with external merge
t3400: restore coverage for note copying with apply backend
Junio C Hamano [Mon, 27 Jul 2026 16:09:00 +0000 (09:09 -0700)]
Merge branch 'ps/copy-wo-the-repository'
The copy_file() and copy_file_with_time() functions have been
refactored to take a repository parameter, allowing the removal of the
implicit dependency on the global 'the_repository' variable in
'copy.c'.
* ps/copy-wo-the-repository:
copy: drop dependency on `the_repository`
The enumeration of untracked and ignored files in 'git status' has
been optimized by avoiding quadratic complexity when inserting into
string lists, reducing the construction cost from O(n^2) to O(n log
n).
* sc/wt-status-avoid-quadratic-insertion:
wt-status: avoid repeated insertion for untracked paths
Junio C Hamano [Mon, 27 Jul 2026 16:09:00 +0000 (09:09 -0700)]
Merge branch 'ps/refspec-wo-the-repository'
The dependency on the global 'the_repository' variable in the
'refspec.c' API has been removed by passing the hash algorithm
explicitly to refspec-parsing functions and storing it in 'struct
refspec'.
* ps/refspec-wo-the-repository:
refspec: stop depending on `the_repository`
refspec: let callers pass in hash algorithm when parsing items
refspec: group related structures and functions
Junio C Hamano [Mon, 27 Jul 2026 16:08:59 +0000 (09:08 -0700)]
Merge branch 'rs/remote-curl-simplify-push-specs'
The passing of push destination specifications in the 'remote-curl'
helper has been simplified by removing the explicit 'count' parameter
and relying on the NULL-termination of the array.
* rs/remote-curl-simplify-push-specs:
remote-curl: simplify passing of push specs
Junio C Hamano [Mon, 27 Jul 2026 16:08:59 +0000 (09:08 -0700)]
Merge branch 'td/ref-filter-memoize-contains'
'git branch --contains' and 'git for-each-ref --contains' have been
optimized to use the memoized commit traversal previously used only by
'git tag --contains', significantly speeding up connectivity checks
across many candidate refs with shared history.
* td/ref-filter-memoize-contains:
commit-reach: die on contains walk errors
ref-filter: memoize --contains with generations
commit-reach: reject cycles in contains walk
Junio C Hamano [Mon, 27 Jul 2026 16:08:59 +0000 (09:08 -0700)]
Merge branch 'ps/refs-wo-the-repository'
The ref subsystem and the worktree API have been refactored to pass a
repository pointer down the call chain, allowing them to drop
references to the global 'the_repository' variable. As part of this,
the handling of the 'core.packedRefsTimeout' configuration has been
moved into the per-repository ref store structure.
* ps/refs-wo-the-repository:
refs: remove remaining uses of `the_repository`
worktree: pass repository to public functions
worktree: pass repository to file-local functions
worktree: refactor code to use available repositories
refs/files: drop `USE_THE_REPOSITORY_VARIABLE`
refs/packed: de-globalize handling of "core.packedRefsTimeout"
Junio C Hamano [Mon, 27 Jul 2026 16:08:58 +0000 (09:08 -0700)]
Merge branch 'jc/submodule-helper-avoid-zu'
An accidental use of the '%zu' format specifier in 'git
submodule--helper' has been corrected to use 'PRIuMAX' and cast the
value to 'uintmax_t' to avoid portability issues.
* jc/submodule-helper-avoid-zu:
submodule--helper: avoid use of %zu for now
Junio C Hamano [Mon, 27 Jul 2026 16:08:58 +0000 (09:08 -0700)]
Merge branch 'ps/shift-root-in-graph'
'git log --graph' has been modified to visually distinguish parentless
'root' commits (and commits that become roots due to history
simplification) by indenting them, preventing them from appearing
falsely related to unrelated commits rendered immediately above them.
* ps/shift-root-in-graph:
graph: add --[no-]graph-indent and log.graphIndent
graph: move config reading into graph_read_config()
graph: wrap cascading commits after 4 columns
graph: indent visual root in graph
graph: add a 2 commit buffer for lookahead
revision: add next_commit_to_show()
lib-log-graph: move check_graph function
rebase: remember fixup -c after skipping fixup/squash
When the final command in a chain of "fixup" and "squash" commands
is skipped, we should prompt the user to edit the commit message
if the chain contains a "fixup -c" command that was not skipped.
Unfortunately, commit_staged_changes() only looks for completed "squash"
commands and so does not prompt the user to edit the message. Fix
this by recording whether a fixup command has the "-c" flag set and
then checking whether we have seen either a "fixup -c" or a "squash"
command. Add regression tests for skipping a command in the middle
of the chain (which currently works but has no test coverage), and
for skipping the final command (which is fixed by this patch).
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
rebase -i: fix counting of fixups after rebase --skip
When the sequencer processes a chain of "fixup" and "squash" commands
it keeps a list of the commands that have been executed. If there are
conflicts, then the list is saved when the rebase stops for the user to
resolve them. When the rebase resumes, the list is loaded and is used
to initialize the count of how many "fixup" and "squash" commands have
been processed; if a command has been skipped with "git rebase --skip",
then the last command needs to be popped off the end of the list.
To count the number of commands, commit_staged_changes() uses the
number of newlines in the file plus one. This is due to the slightly
unusual way the list is constructed - instead of appending a newline
when a command is added, a newline is inserted before the command
if the current count is greater than zero. Therefore, when we pop a
skipped command off the list, we should also remove the newline that
precedes it. Otherwise, when a new command is added, a blank line
will be left before it, which will contribute to the fixup count the
next time the file is read. Unfortunately, the preceding newline is
not removed, leading to an incorrect count. Fix this by removing the
newline that appears before the skipped command.
In addition to fixing the code that removes a skipped command from the
list, the code that reads the list is fixed to skip blank lines. We
have had reports of users starting a rebase with one version of
git and continuing it with another. Often this happens because the
version of git bundled with an IDE or TUI differs from the one used
at the command line. By fixing both the reading and writing ends of
the problem we ensure the count is correct when an older version of
git reads the fixup file written by a newer version and vice versa.
Triggering the incorrect count requires the user to skip two "fixup" or
"squash" commands before the final command in the chain. An existing
test is extended to prevent future regressions. The consequence of
miscounting is not serious: we just print the wrong count in the
header of the commit message template.
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Sun, 26 Jul 2026 08:39:05 +0000 (04:39 -0400)]
ci: bump ubuntu image version for static-analysis job
We recently ran into a case[1] where old versions of coccinelle ran very
slowly, but newer ones are fine. The version we use in GitHub's CI was
the old slow version, leading to timeouts of the static-analysis job.
We get the old version because we ask for the ubuntu-22.04 image. That
has coccinelle 1.1.1, but the "fast" improvement is in coccinelle 1.3.0,
specifically their 58619b8fe (break up envs for e1 & e2, 2024-08-18).
Bumping to ubuntu-25.10 would be enough to get that new version. But I
don't see any need to ask for a specific version at all. We originally
used a specific version because coccinelle wasn't available in ubuntu
20.04, so we pinned to 18.04 in d051ed77ee (.github/workflows/main.yml:
run static-analysis on bionic, 2021-02-08). Later that got bumped in ef46584831 (ci: update 'static-analysis' to Ubuntu 22.04, 2022-08-23)
when 18.04 support was dropped.
It seems like the absence of coccinelle was a blip in 20.04, and we can
just stick with "latest" going forward.
I tested the result on GitHub's CI. I bumped the matching line in the
GitLab definition, but didn't have a simple means of testing (but it's
such a trivial change nothing could go wrong, right?).
Using gcc 15, compiling with CHECK_ASSERTION_SIDE_EFFECTS=1 causes a
complaint about this line in bloom.c having a side effect:
assert(version == 1 || version == 2);
I think this is pretty clearly a false positive, as those comparisons
should not have side effects. The side-effect checker uses a magic
definition of assert() that relies on the compiler's optimizer to drop a
reference to an otherwise unused variable. And for whatever reason, gcc
chooses not to do so here under -O2 (side note: if you have -O0 in your
CFLAGS, that naturally creates many more false positives!).
This code has been around for a while, but nobody seems to have noticed
because we use an older version of the compiler in our static-analysis
ci job, and it does not complain. Presumably very few people run this
check locally on their more modern compilers.
Let's silence the false positive to avoid confusion for anyone running
locally, and to make it possible to upgrade the image we use for our
static-analysis job.
We could just switch to our custom ASSERT() here, but I think we can
improve the code by integrating the assertion into the if/else cascade.
That avoids repeating the logic about which versions are acceptable.
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano [Sat, 25 Jul 2026 16:03:24 +0000 (09:03 -0700)]
remote: plug memory leaks
The in-core data structure used to keep track of
'url.<real>.{insteadOf,pushInsteadOf} = <alias>' settings is not
properly cleaned up when the process is done with it.
'struct rewrites' is embedded in 'remote_state' and serves as the
top level of the rewrite data. This holds an array of a variable
number of pointers to 'struct rewrite' allocated individually on the
heap. Each 'struct rewrite' holds a '.base' string and an array of
'struct counted_string' called '.instead_of', which is allocated
contiguously on the heap. Each 'struct counted_string' has a
pointer to a string allocated on the heap.
Amid these pointers, rewrites_release() fails to free everything
other than 'struct rewrite''s '.base' member and the 'struct rewrite'
instances themselves.
Fix rewrites_release() to also free the contiguous array storing
'.instead_of', the string pointers within each '.instead_of' element,
and each 'struct rewrite' instance individually allocated on the heap.
René Scharfe [Sat, 25 Jul 2026 10:41:07 +0000 (12:41 +0200)]
branch: report active bisect run when rejecting delete
git branch refuses to delete branches that are currently checked out
with a message like this: "error: cannot delete branch 'foo' used by
worktree at '/path/of/worktree'". This can be confusing if it's an
internal checkout for git bisect. Report a more specific error in
that case to help users that might have forgotten their bisect run.
Suggested-by: stsp <stsp2@yandex.ru> Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Adrian Friedli [Fri, 24 Jul 2026 12:41:38 +0000 (14:41 +0200)]
builtin/clone: fix segfault when using --revision with protocol v0
Servers supporting protocol v2 do not advertise excess refs and honor
`transport_ls_refs_options.ref_prefixes` when
$ git clone --revision=refs/heads/main $URL
contacts them, but when talking to a server that does not support
protocol v2 the client segfaults. This can also be observed when v0 is
enforced for example by
In the protocol v2 case the server honors
`transport_ls_refs_options.ref_prefixes` and in `cmd_clone()` the linked
list `refs` returned by `transport_get_remote_refs()` only contains a
single item, which is the ref requested with the --revision argument.
Both `remote_head` returned by `find_ref_by_name()` and
`remote_head_points_at` returned by `guess_remote_head()` are NULL. The
guard in `update_remote_refs()` skips a the affected code because
`remote_head_points_at` is NULL.
In the protocol v0 case in `cmd_clone()` the linked list `refs` returned
by `transport_get_remote_refs()` contains many items, amongst others
"HEAD". `remote_head` returned by `find_ref_by_name()` is not NULL and
`remote_head_points_at` returned by `guess_remote_head()` is not NULL
but its field `peer_ref` is NULL. Because `remote_head_points_at` is not
NULL the guard in `update_remote_refs()` does not skip the affected code
and `remote_head_points_at->peer_ref->name` is accessed, which causes a
segfault later on.
Signed-off-by: Adrian Friedli <adrian.friedli@mt.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>