80300 Commits

Author SHA1 Message Date
Junio C Hamano
98c4212af2 Merge branch 'lp/diff-stat-utf8-display-width-fix' into jch
"git log --graph --stat" did not count the display width of colored
graph part of its own output correctly, which has been corrected.

* lp/diff-stat-utf8-display-width-fix:
  t4052: test for diffstat width when prefix contains ANSI escape codes
  diff: handle ANSI escape codes in prefix when calculating diffstat width
2026-03-07 22:27:28 -08:00
Junio C Hamano
4117808f22 Merge branch 'jh/alias-i18n-fixes' (early part) into jch
* 'jh/alias-i18n-fixes' (early part):
  git, help: fix memory leaks in alias listing
  alias: treat empty subsection [alias ""] as plain [alias]
  doc: fix list continuation in alias subsection example
2026-03-07 22:27:27 -08:00
Junio C Hamano
14199d8286 Merge branch 'cs/add-skip-submodule-ignore-all' into jch
"git add <submodule>" has been taught to honor
submodule.<name>.ignore that is set to "all" (and requires "git add
-f" to override it).

* cs/add-skip-submodule-ignore-all:
  Documentation: update add --force option + ignore=all config
  tests: fix existing tests when add an ignore=all submodule
  tests: t2206-add-submodule-ignored: ignore=all and add --force tests
  read-cache: submodule add need --force given ignore=all configuration
  read-cache: update add_files_to_cache take param ignored_too
2026-03-07 22:27:27 -08:00
Junio C Hamano
beaaec9b19 Merge branch 'ps/refs-for-each' into jch
Code refactoring around refs-for-each-* API functions.

* ps/refs-for-each:
  refs: replace `refs_for_each_fullref_in()`
  refs: replace `refs_for_each_namespaced_ref()`
  refs: replace `refs_for_each_glob_ref()`
  refs: replace `refs_for_each_glob_ref_in()`
  refs: replace `refs_for_each_rawref_in()`
  refs: replace `refs_for_each_rawref()`
  refs: replace `refs_for_each_ref_in()`
  refs: improve verification for-each-ref options
  refs: generalize `refs_for_each_fullref_in_prefixes()`
  refs: generalize `refs_for_each_namespaced_ref()`
  refs: speed up `refs_for_each_glob_ref_in()`
  refs: introduce `refs_for_each_ref_ext`
  refs: rename `each_ref_fn`
  refs: rename `do_for_each_ref_flags`
  refs: move `do_for_each_ref_flags` further up
  refs: move `refs_head_ref_namespaced()`
  refs: remove unused `refs_for_each_include_root_ref()`
2026-03-07 22:27:27 -08:00
Junio C Hamano
422aaeff37 Merge branch 'ar/config-hooks' (early part) into jch
* 'ar/config-hooks' (early part):
  hook: add -z option to "git hook list"
  hook: allow out-of-repo 'git hook' invocations
  hook: allow event = "" to overwrite previous values
  hook: allow disabling config hooks
  hook: include hooks from the config
  hook: add "git hook list" command
  hook: run a list of hooks to prepare for multihook support
  hook: add internal state alloc/free callbacks
2026-03-07 22:27:26 -08:00
Junio C Hamano
cb438743fe Merge branch 'hn/status-compare-with-push' (early part) into jch
* 'hn/status-compare-with-push' (early part):
  status: add status.compareBranches config for multiple branch comparisons
  refactor format_branch_comparison in preparation
2026-03-07 22:27:26 -08:00
Junio C Hamano
ac5484a33b Merge branch 'ar/run-command-hook-take-2' into jch
Use the hook API to replace ad-hoc invocation of hook scripts via
the run_command() API.

* ar/run-command-hook-take-2:
  builtin/receive-pack: avoid spinning no-op sideband async threads
  receive-pack: convert receive hooks to hook API
  receive-pack: convert update hooks to new API
  run-command: poll child input in addition to output
  hook: add jobs option
  reference-transaction: use hook API instead of run-command
  transport: convert pre-push to hook API
  hook: allow separate std[out|err] streams
  hook: convert 'post-rewrite' hook in sequencer.c to hook API
  hook: provide stdin via callback
  run-command: add stdin callback for parallelization
  run-command: add helper for pp child states
  t1800: add hook output stream tests
2026-03-07 22:27:26 -08:00
Francesco Paparatto
d3edca979a t3310: avoid hiding failures from rev-parse in command substitutions
Running `git` commands inside command substitutions like

    test "$(git rev-parse A)" = "$(git rev-parse B)"

can hide failures from the `git` invocations and provide little
diagnostic information when `test` fails.

Use `test_cmp` when comparing against a stored expected value so
mismatches show both expected and actual output. Use `test_cmp_rev`
when comparing two revisions. These helpers produce clearer failure
output, making it easier to understand what went wrong.

Suggested-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Francesco Paparatto <francescopaparatto@gmail.com>
Reviewed-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-07 22:04:36 -08:00
Omri Sarig
beca0ca4be doc: make it easier to find custom command information
Git supports creating additional commands through aliases, and through
placement of executables with a "git-" prefix in the PATH.

This information was not easy enough to find - users will look for this
information around the command description, but the documentation
exists in other locations.

Update the "GIT COMMANDS" section to reference the relevant sections,
making it easier for to find this information.

Signed-off-by: Omri Sarig <omri.sarig13@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-07 14:04:23 -08:00
Jeff King
a8a69bbb64 meson: turn on NO_MMAP when building with LSan
The previous commit taught the Makefile to turn on NO_MMAP in this
instance. We should do the same with meson for consistency. We already
do this for ASan builds, so we can just tweak one conditional.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 21:12:10 -08:00
Jeff King
00611d86c6 Makefile: turn on NO_MMAP when building with LSan
The past few commits fixed some cases where we leak memory allocated by
mmap(). Building with SANITIZE=leak doesn't detect these because it
covers only heap buffers allocated by malloc().

But if we build with NO_MMAP, our compat mmap() implementation will
allocate a heap buffer and pread() into it. And thus Lsan will detect
these leaks for free.

Using NO_MMAP is less performant, of course, since we have to use extra
memory and read in the whole file, rather than faulting in pages from
disk. But LSan builds are already slow, and this doesn't make them
measurably worse. Getting extra coverage for our leak-checking is worth
it.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 21:12:10 -08:00
Jeff King
b68e875bec object-file: fix mmap() leak in odb_source_loose_read_object_stream()
We mmap() a loose object file, storing the result in the local variable
"mapped", which is eventually assigned into our stream struct as
"st.mapped". If we hit an error, we jump to an error label which does:

  munmap(st.mapped, st.mapsize);

to clean up. But this is wrong; we don't assign st.mapped until the end
of the function, after all of the "goto error" jumps. So this munmap()
is never cleaning up anything (st.mapped is always NULL, because we
initialize the struct with calloc).

Instead, we should feed the local variable to munmap().

This leak is due to 595296e124 (streaming: allocate stream inside the
backend-specific logic, 2025-11-23), which introduced the local
variable. Before that, we assigned the mmap result directly into
st.mapped. It was probably switched there so that we do not have to
allocate/free the struct when the map operation fails (e.g., because we
don't have the loose object). Before that commit, the struct was passed
in from the caller, so there was no allocation at all.

You can see the leak in the test suite by building with:

  make SANITIZE=leak NO_MMAP=1 CC=clang

and running t1060. We need NO_MMAP so that the mmap() is backed by an
actual malloc(), which allows LSan to detect it. And the leak seems not
to be detected when compiling with gcc, probably due to some internal
compiler decisions about how the stack memory is written.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 21:11:32 -08:00
Michael Montalbo
09138ccff0 doc: note that -L supports patch formatting and pickaxe options
Now that -L output flows through the standard diff pipeline,
document that patch formatting options like --word-diff,
--color-moved, --no-prefix, whitespace handling (-w, -b),
and pickaxe options (-S, -G) are supported.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 18:13:03 -08:00
Michael Montalbo
45dc576aba t4211: add tests for -L with standard diff options
Now that -L output flows through the standard diff pipeline, verify
that previously-ignored diff options work: formatting (--word-diff,
--word-diff-regex, --no-prefix, --src/dst-prefix, --full-index,
--abbrev), whitespace handling (-w, -b), output indicators
(--output-indicator-new/old/context), direction reversal (-R),
--color-moved, and pickaxe options (-S, -G).

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 18:13:02 -08:00
Michael Montalbo
5482184c52 line-log: route -L output through the standard diff pipeline
`git log -L` has always bypassed the standard diff pipeline.
`dump_diff_hacky()` in line-log.c hand-rolls its own diff headers and
hunk output, which means most diff formatting options are silently
ignored.  A NEEDSWORK comment has acknowledged this since the feature
was introduced:

    /*
     * NEEDSWORK: manually building a diff here is not the Right
     * Thing(tm).  log -L should be built into the diff pipeline.
     */

Remove `dump_diff_hacky()` and its helpers and route -L output through
`builtin_diff()` / `fn_out_consume()`, the same path used by `git diff`
and `git log -p`.  The mechanism is a pair of callback wrappers that sit
between `xdi_diff_outf()` and `fn_out_consume()`, filtering xdiff's
output to only the tracked line ranges.  To ensure xdiff emits all lines
within each range as context, the context length is inflated to span the
largest range.

Wire up the `-L` implies `--patch` default in revision setup rather
than forcing it at output time, so `line_log_print()` is just
`diffcore_std()` + `diff_flush()` with no format save/restore.
Rename detection is a no-op since pairs are already resolved during
the history walk in `queue_diffs()`, but running `diffcore_std()`
means `-S`/`-G` (pickaxe), `--orderfile`, and `--diff-filter` now
work with `-L`, and `diff_resolve_rename_copy()` sets pair statuses
correctly without manual assignment.

Switch `diff_filepair_dup()` from `xmalloc` to `xcalloc` so that new
fields (including `line_ranges`) are zero-initialized by default.

As a result, diff formatting options that were previously silently
ignored (e.g. --word-diff, --no-prefix, -w, --color-moved) now work
with -L, and output gains `index` lines, `new file mode` headers, and
funcname context in `@@` headers.  This is a user-visible output change:
tools that parse -L output may need to handle the additional header
lines.

The context-length inflation means xdiff may process more output than
needed for very wide line ranges, but benchmarks on files up to 7800
lines show no measurable regression.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 18:13:02 -08:00
Michael Montalbo
984677c96b line-log: fix crash when combined with pickaxe options
queue_diffs() passes the caller's diff_options, which may carry
user-specified pickaxe state, to diff_tree_oid() and diffcore_std()
when detecting renames for line-level history tracking.  When pickaxe
options are present on the command line (-G and -S to filter by text
pattern, --find-object to filter by object identity), diffcore_std()
also runs diffcore_pickaxe(), which may discard diff pairs that are
relevant for rename detection.  Losing those pairs breaks rename
following.

Before a2bb801f6a (line-log: avoid unnecessary full tree diffs,
2019-08-21), this silently truncated history at rename boundaries.
That commit moved filter_diffs_for_paths() inside the rename-
detection block, so it only runs when diff_might_be_rename() returns
true.  When pickaxe discards a rename pair, the rename goes
undetected, and a deletion pair at a subsequent commit passes
through uncleaned, reaching process_diff_filepair() with an invalid
filespec and triggering an assertion failure.

Fix this by building a private diff_options for the rename-detection
path inside queue_diffs(), following the same pattern used by blame's
find_rename().  This isolates the rename machinery from unrelated
user-specified options.

Reported-by: Matthew Hughes <matthewhughes934@gmail.com>
Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 18:13:00 -08:00
Alan Braithwaite
79d2167e8d clone: add clone.<url>.defaultObjectFilter config
Add a new configuration option that lets users specify a default
partial clone filter, optionally scoped by URL pattern.  When
cloning a repository whose URL matches a configured pattern,
git-clone automatically applies the filter, equivalent to passing
--filter on the command line.

    [clone]
        defaultObjectFilter = blob:limit=1m

    [clone "https://github.com/"]
        defaultObjectFilter = blob:limit=5m

    [clone "https://internal.corp.com/large-project/"]
        defaultObjectFilter = blob:none

The bare clone.defaultObjectFilter applies to all clones.  The
URL-qualified form clone.<url>.defaultObjectFilter restricts the
setting to matching URLs.  URL matching uses the existing
urlmatch_config_entry() infrastructure, following the same rules as
http.<url>.* — a domain, namespace, or specific project can be
matched, and the most specific match wins.

The config only affects the initial clone.  Once the clone completes,
the filter is recorded in remote.<name>.partialCloneFilter, so
subsequent fetches inherit it automatically.  An explicit --filter
on the command line takes precedence, and --no-filter defeats the
configured default entirely.

Signed-off-by: Alan Braithwaite <alan@braithwaite.dev>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 18:02:28 -08:00
Mirko Faina
51ed9f7e72 docs: add usage for the cover-letter fmt feature
Document the new "--cover-letter-format" option in format-patch and its
related configuration variable "format.commitListFormat".

Signed-off-by: Mirko Faina <mroik@delayed.space>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 17:16:45 -08:00
Mirko Faina
be0ef6fcd2 format-patch: add commitListFormat config
Using "--cover-letter" we can tell format-patch to generate a cover
letter, in this cover letter there's a list of commits included in the
patch series and the format is specified by the "--cover-letter-format"
option. Would be useful if this format could be configured from the
config file instead of always needing to pass it from the command line.

Teach format-patch how to read the format spec for the cover letter from
the config files. The variable it should look for is called
format.commitListFormat.

Possible values:
  - commitListFormat is set but no string is passed: it will default to
    "[%(count)/%(total)] %s"

  - if a string is passed: will use it as a format spec. Note that this
    is either "shortlog" or a format spec prefixed by "log:"
    e.g."log:%s (%an)"

  - if commitListFormat is not set: it will default to the shortlog
    format.

Signed-off-by: Mirko Faina <mroik@delayed.space>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 17:16:44 -08:00
Mirko Faina
6005932d95 format-patch: add ability to use alt cover format
Often when sending patch series there's a need to clarify to the
reviewer what's the purpose of said series, since it might be difficult
to understand it from reading the commits messages one by one.

"git format-patch" provides the useful "--cover-letter" flag to declare
if we want it to generate a template for us to use. By default it will
generate a "git shortlog" of the changes, which developers find less
useful than they'd like, mainly because the shortlog groups commits by
author, and gives no obvious chronological order.

Give format-patch the ability to specify an alternative format spec
through the "--cover-letter-format" option. This option either takes
"shortlog", which is the current format, or a format spec prefixed with
"log:".

Example:
    git format-patch --cover-letter \
        --cover-letter-format="log:[%(count)/%(total)] %s (%an)" HEAD~3

    [1/3] this is a commit summary (Mirko Faina)
    [2/3] this is another commit summary (Mirko Faina)
    ...

Signed-off-by: Mirko Faina <mroik@delayed.space>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 17:16:44 -08:00
Mirko Faina
2af59cbcf4 format-patch: move cover letter summary generation
As of now format-patch allows generation of a template cover letter for
patch series through "--cover-letter".

Move shortlog summary code generation to its own function. This is done
in preparation to other patches where we enable the user to format the
commit list using thier own format string.

Signed-off-by: Mirko Faina <mroik@delayed.space>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 17:16:44 -08:00
Mirko Faina
9ccd9e6fca pretty.c: add %(count) and %(total) placeholders
In many commands we can customize the output through the "--format" or
the "--pretty" options. This patch adds two new placeholders used mainly
when there's a range of commits that we want to show.

Currently these two placeholders are not usable as they're coupled with
the rev_info->nr and rev_info->total fields, fields that are used only
by the format-patch numbered email subjects.

Teach repo_format_commit_message() the %(count) and %(total)
placeholders.

Signed-off-by: Mirko Faina <mroik@delayed.space>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 17:16:44 -08:00
Justin Tobler
a190f01f57 Documentation: extend guidance for submitting patches
Before submitting patches on the mailing list, it is often a good idea
to check for previous related discussions or if similar work is already
in progress. This enables better coordination amongst contributors and
could avoid duplicating work.

Additionally, it is often recommended to give reviewers some time to
reply to a patch series before sending new versions. This helps collect
broader feedback and reduces unnecessary churn from rapid rerolls.

Document this guidance in "Documentation/SubmittingPatches" accordingly.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 14:27:05 -08:00
Colin Stagner
c30871b91d contrib/subtree: reduce recursion during split
On Debian-alikes, POSIX sh has a hardcoded recursion depth
of 1000. This limit operates like bash's `$FUNCNEST` [1], but
it does not actually respect `$FUNCNEST`. This is non-standard
behavior. On other distros, the sh recursion depth is limited
only by the available stack size.

With certain history graphs, subtree splits are recursive—with
one recursion per commit. Attempting to split complex repos that
have thousands of commits, like [2], may fail on these distros.

Reduce the amount of recursion required by eagerly discovering
the complete range of commits to process.

The recursion is a side-effect of the rejoin-finder in
`find_existing_splits`. Rejoin mode, as in

    git subtree split --rejoin -b hax main ...

improves the speed of later splits by merging the split history
back into `main`. This gives the splitting algorithm a stopping
point. The rejoin maps one commit on `main` to one split commit
on `hax`. If we encounter this commit, we know that it maps to
`hax`.

But this is only a single point in the history. Many splits
require history from before the rejoin. See patch content for
examples.

If pre-rejoin history is required, `check_parents` recursively
discovers each individual parent, with one recursion per commit.
The recursion deepens the entire tree, even if an older rejoin
is available. This quickly overwhelms the Debian sh stack.

Instead of recursively processing each commit, process *all* the
commits back to the next obvious starting point: i.e., either the
next-oldest --rejoin or the beginning of history. This is where the
recursion is likely to stop anyway.

While this still requires recursion, it is *considerably* less
recursive.

[1]: https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html#index-FUNCNEST

[2]: https://github.com/christian-heusel/aur.git

Signed-off-by: Colin Stagner <ask+git@howdoi.land>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:57:48 -08:00
Colin Stagner
3b3ace4d5b contrib/subtree: functionalize split traversal
`git subtree split` requires an ancestor-first history traversal.
Refactor the existing rev-list traversal into its own function,
`find_commits_to_split`.

Pass unrevs via stdin to avoid limits on the maximum length of
command-line arguments. Also remove an unnecessary `eval`.

Signed-off-by: Colin Stagner <ask+git@howdoi.land>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:57:48 -08:00
Colin Stagner
f8e90b972e contrib/subtree: reduce function side-effects
`process_subtree_split_trailer()` communicates its return value
to the caller by setting a variable (`sub`) that is also defined
by the calling function. This is both unclear and encourages
side-effects.

Invoke this function in a sub-shell instead.

Signed-off-by: Colin Stagner <ask+git@howdoi.land>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:57:48 -08:00
Johannes Schindelin
818fbfd208 sideband: delay sanitizing by default to Git v3.0
The sideband sanitization patches allow ANSI color sequences through
by default, preserving compatibility with pre-receive hooks that
provide colored output during `git push`.

Even so, there is concern that changing any default behavior in a
minor release may have unforeseen consequences. To accommodate this,
defer the secure-by-default behavior to Git v3.0, where breaking
changes are expected.

This gives users and tooling time to prepare, while committing to
address CVE-2024-52005 in Git v3.0.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
[jc: adjusted for the removal of 'default' value]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:54:05 -08:00
Junio C Hamano
82b67e8bd9 Merge branch 'jc/neuter-sideband-fixup' into jc/neuter-sideband-post-3.0
* jc/neuter-sideband-fixup:
  sideband: drop 'default' configuration
  sideband: offer to configure sanitizing on a per-URL basis
  sideband: add options to allow more control sequences to be passed through
  sideband: do allow ANSI color sequences by default
  sideband: introduce an "escape hatch" to allow control characters
  sideband: mask control characters
2026-03-06 13:53:08 -08:00
Junio C Hamano
826cc47220 sideband: drop 'default' configuration
The topic so far allows users to tweak the configuration variable
sideband.allowControlCharacters to override the hardcoded default,
but among which there is the value called 'default'.  The plan [*]
of the series is to loosen the setting by a later commit in the
series and schedule it to tighten at the Git 3.0 boundary for end
users, at which point, the meaning of this 'default' value will
change.

Which is a dubious design.

A user expresses their preference by setting configuration variable
in order to guard against sudden change brought in by changes to the
hardcoded default behaviour, and letting them set it to 'default'
that will change at the Git 3.0 boundary defeats its purpose.  If a
user wants to say "I am easy and can go with whatever hardcoded
default Git implementors choose for me", they simply leave the
configuration variable unspecified.

Let's remove it from the state before Git 3.0 so that those users
who set it to 'default' will not see the behaviour changed under
their feet all of sudden.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:52:28 -08:00
Johannes Schindelin
602c83f0ef sideband: offer to configure sanitizing on a per-URL basis
The main objection against sanitizing the sideband that was raised
during the review of the sideband sanitizing patches, first on the
git-security mailing list, then on the public mailing list, was that
there are some setups where server-side `pre-receive` hooks want to
error out, giving colorful messages to the users on the client side (if
they are not redirecting the output into a file, that is).

To avoid breaking such setups, the default chosen by the sideband
sanitizing patches is to pass through ANSI color sequences.

Still, there might be some use case out there where that is not enough.
Therefore the `sideband.allowControlCharacters` config setting allows
for configuring  levels of sanitizing.

As Junio Hamano pointed out, to keep users safe by default, we need to
be able to scope this to some servers because while a user may trust
their company's Git server, the same might not apply to other Git
servers.

To allow for this, let's imitate the way `http.<url>.*` offers
to scope config settings to certain URLs, by letting users
override the `sideband.allowControlCharacters` setting via
`sideband.<url>.allowControlCharacters`.

Suggested-by: Junio Hamano <gitster@pobox.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:52:28 -08:00
Johannes Schindelin
128914438a sideband: add options to allow more control sequences to be passed through
Even though control sequences that erase characters are quite juicy for
attack scenarios, where attackers are eager to hide traces of suspicious
activities, during the review of the side band sanitizing patch series
concerns were raised that there might be some legimitate scenarios where
Git server's `pre-receive` hooks use those sequences in a benign way.

Control sequences to move the cursor can likewise be used to hide tracks
by overwriting characters, and have been equally pointed out as having
legitimate users.

Let's add options to let users opt into passing through those ANSI
Escape sequences: `sideband.allowControlCharacters` now supports also
`cursor` and `erase`, and it parses the value as a comma-separated list.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:52:28 -08:00
Johannes Schindelin
12f0fda905 sideband: do allow ANSI color sequences by default
The preceding two commits introduced special handling of the sideband
channel to neutralize ANSI escape sequences before sending the payload
to the terminal, and `sideband.allowControlCharacters` to override that
behavior.

However, as reported by brian m. carlson, some `pre-receive` hooks that
are actively used in practice want to color their messages and therefore
rely on the fact that Git passes them through to the terminal, even
though they have no way to determine whether the receiving side can
actually handle Escape sequences (think e.g. about the practice
recommended by Git that third-party applications wishing to use Git
functionality parse the output of Git commands).

In contrast to other ANSI escape sequences, it is highly unlikely that
coloring sequences can be essential tools in attack vectors that mislead
Git users e.g. by hiding crucial information.

Therefore we can have both: Continue to allow ANSI coloring sequences to
be passed to the terminal by default, and neutralize all other ANSI
Escape sequences.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:52:28 -08:00
Johannes Schindelin
9ed1625a58 sideband: introduce an "escape hatch" to allow control characters
The preceding commit fixed the vulnerability whereas sideband messages
(that are under the control of the remote server) could contain ANSI
escape sequences that would be sent to the terminal verbatim.

However, this fix may not be desirable under all circumstances, e.g.
when remote servers deliberately add coloring to their messages to
increase their urgency.

To help with those use cases, give users a way to opt-out of the
protections: `sideband.allowControlCharacters`.

Suggested-by: brian m. carlson <sandals@crustytoothpaste.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:52:28 -08:00
Johannes Schindelin
0494953820 sideband: mask control characters
The output of `git clone` is a vital component for understanding what
has happened when things go wrong. However, these logs are partially
under the control of the remote server (via the "sideband", which
typically contains what the remote `git pack-objects` process sends to
`stderr`), and is currently not sanitized by Git.

This makes Git susceptible to ANSI escape sequence injection (see
CWE-150, https://cwe.mitre.org/data/definitions/150.html), which allows
attackers to corrupt terminal state, to hide information, and even to
insert characters into the input buffer (i.e. as if the user had typed
those characters).

To plug this vulnerability, disallow any control character in the
sideband, replacing them instead with the common `^<letter/symbol>`
(e.g. `^[` for `\x1b`, `^A` for `\x01`).

There is likely a need for more fine-grained controls instead of using a
"heavy hammer" like this, which will be introduced subsequently.

Helped-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:52:28 -08:00
Jeff King
e2f1139240 pack-revindex: avoid double-loading .rev files
The usual entry point for loading the pack revindex is the
load_pack_revindex() function. It returns immediately if the packed_git
has a non-NULL revindex or revindex data field (representing an
in-memory or mmap'd .rev file, respectively), since the data is already
loaded.

But in 5a6072f631 (fsck: validate .rev file header, 2023-04-17) the fsck
code path switched to calling load_pack_revindex_from_disk() directly,
since it wants to check the on-disk data (if there is any). But that
function does _not_ check to see if the data has already been loaded; it
just maps the file, overwriting the revindex_map pointer (and pointing
revindex_data inside that map). And in that case we've leaked the mmap()
pointed to by revindex_map (if it was non-NULL).

This usually doesn't happen, since fsck wouldn't need to load the
revindex for any reason before we get to these checks. But there are
some cases where it does. For example, is_promisor_object() runs
odb_for_each_object() with the PACK_ORDER flag, which uses the revindex.

This happens a few times in our test suite, but SANITIZE=leak doesn't
detect it because we are leaking an mmap(), not a heap-allocated buffer
from malloc(). However, if you build with NO_MMAP, then our compat mmap
will read into a heap buffer instead, and LSan will complain. This
causes failures in t5601, t0410, t5702, and t5616.

We can fix it by checking for existing revindex_data when loading from
disk. This is redundant when we're called from load_pack_revindex(), but
it's a cheap check. The alternative is to teach check_pack_rev_indexes()
in fsck to skip the load, but that seems messier; it doesn't otherwise
know about internals like revindex_map and revindex_data.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:51:20 -08:00
Jeff King
0921da1724 check_connected(): fix leak of pack-index mmap
Since c6807a40dc (clone: open a shortcut for connectivity check,
2013-05-26), we may open a one-off packed_git struct to check what's in
the pack we just received. At the end of the function we throw away the
struct (rather than linking it into the repository struct as usual).

We used to leak the struct until dd4143e7bf (connected.c: free the
"struct packed_git", 2022-11-08), which calls free(). But that's not
sufficient; inside the struct we'll have mmap'd the pack idx data from
disk, which needs an munmap() call.

Building with SANITIZE=leak doesn't detect this, because we are leaking
our own mmap(), and it only finds heap allocations from malloc(). But if
we use our compat mmap implementation like this:

  make NO_MMAP=MapsBecomeMallocs SANITIZE=leak

then LSan will notice the leak, because now it's a regular heap buffer
allocated by malloc().

We can fix it by calling close_pack(), which will free any associated
memory. Note that we need to check for NULL ourselves; unlike free(), it
is not safe to pass a NULL pointer to close_pack().

Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:51:03 -08:00
Jeff King
26b974b3a9 check_connected(): delay opening new_pack
In check_connected(), if the transport tells us we got a single packfile
that has already been verified as self-contained and connected, then we
can skip checking connectivity for any tips that are mentioned in that
pack. This goes back to c6807a40dc (clone: open a shortcut for
connectivity check, 2013-05-26).

We don't need to open that pack until we are about to start sending oids
to our child rev-list process, since that's when we check whether they
are in the self-contained pack. Let's push the opening of that pack
further down in the function. That saves us from having to clean it up
when we leave the function early (and by the time have opened the
rev-list process, we never leave the function early, since we have to
clean up the child process).

Signed-off-by: Jeff King <peff@peff.net>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:51:03 -08:00
Li Chen
e4f9d6b0ab rebase: support --trailer
Add a new --trailer=<trailer> option to git rebase to append trailer
lines to each rewritten commit message (merge backend only).

Because the apply backend does not provide a commit-message filter,
reject --trailer when --apply is in effect and require the merge backend
instead.

This option implies --force-rebase so that fast-forwarded commits are
also rewritten. Validate trailer arguments early to avoid starting an
interactive rebase with invalid input.

Add integration tests covering error paths and trailer insertion across
non-interactive and interactive rebases.

Signed-off-by: Li Chen <me@linux.beauty>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:02:20 -08:00
Li Chen
5e148696bf commit, tag: parse --trailer with OPT_STRVEC
Now that amend_file_with_trailers() expects raw trailer lines, do not
store argv-style "--trailer=<trailer>" strings in git commit and git
tag.

Parse --trailer using OPT_STRVEC so trailer_args contains only the
trailer value, and drop the temporary prefix stripping in
amend_file_with_trailers().

Signed-off-by: Li Chen <me@linux.beauty>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:02:20 -08:00
Li Chen
6b2243fdd4 trailer: append trailers without fork/exec
Introduce amend_strbuf_with_trailers() to apply trailer additions to a
message buffer via process_trailers(), avoiding the need to run git
interpret-trailers as a child process.

Update amend_file_with_trailers() to use the in-process helper and
rewrite the target file via tempfile+rename, preserving the previous
in-place semantics. As the trailers are no longer added in a separate
process and trailer_config_init() die()s on missing config values it
is called early on in cmd_commit() and cmd_tag() so that they die()
early before writing the message file. The trailer arguments are now
also sanity checked.

Keep existing callers unchanged by continuing to accept argv-style
--trailer=<trailer> entries and stripping the prefix before feeding the
in-process implementation.

Signed-off-by: Li Chen <me@linux.beauty>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:02:20 -08:00
Li Chen
a4fd4c5234 trailer: libify a couple of functions
Move create_in_place_tempfile() and process_trailers() from
builtin/interpret-trailers.c into trailer.c and expose it via trailer.h.

This reverts most of ae0ec2e0e0 (trailer: move interpret_trailers()
to interpret-trailers.c, 2024-03-01) and lets other call sites reuse
the same trailer rewriting logic.

Signed-off-by: Li Chen <me@linux.beauty>
Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:02:19 -08:00
Phillip Wood
876b2ebee2 interpret-trailers: refactor create_in_place_tempfile()
Refactor create_in_place_tempfile() in preparation for moving it
to tralier.c. Change the return type to return a `struct tempfile*`
instead of a `FILE*` so that we can remove the file scope tempfile
variable. Since 076aa2cbda (tempfile: auto-allocate tempfiles on
heap, 2017-09-05) it has not been necessary to make tempfile varibales
static so this is safe. Also use error() and return NULL in place of
die() so the caller can exit gracefully and use find_last_dir_sep()
rather than strchr() to find the parent directory.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:02:19 -08:00
Li Chen
8efabc9e64 interpret-trailers: factor trailer rewriting
Extract the trailer rewriting logic into a helper that appends to an
output strbuf.

Update interpret_trailers() to handle file I/O only: read input once,
call the helper, and write the buffered result.

This separation makes it easier to move the helper into trailer.c in the
next commit.

Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk>
Signed-off-by: Li Chen <me@linux.beauty>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-06 13:02:19 -08:00
Patrick Steinhardt
d6fc6fe6f8 odb/source: make begin_transaction() function pluggable
Introduce a new callback function in `struct odb_source` to make the
function pluggable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-05 11:45:17 -08:00
Patrick Steinhardt
eb9635d83b odb/source: make write_alternate() function pluggable
Introduce a new callback function in `struct odb_source` to make the
function pluggable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-05 11:45:17 -08:00
Patrick Steinhardt
7ae23630c3 odb/source: make read_alternates() function pluggable
Introduce a new callback function in `struct odb_source` to make the
function pluggable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-05 11:45:16 -08:00
Patrick Steinhardt
fc7fb0ef35 odb/source: make write_object_stream() function pluggable
Introduce a new callback function in `struct odb_source` to make the
function pluggable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-05 11:45:16 -08:00
Patrick Steinhardt
6e76c3ab69 odb/source: make write_object() function pluggable
Introduce a new callback function in `struct odb_source` to make the
function pluggable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-05 11:45:16 -08:00
Patrick Steinhardt
6a38b13fba odb/source: make freshen_object() function pluggable
Introduce a new callback function in `struct odb_source` to make the
function pluggable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-05 11:45:16 -08:00
Patrick Steinhardt
fdefdc2e69 odb/source: make for_each_object() function pluggable
Introduce a new callback function in `struct odb_source` to make the
function pluggable.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-05 11:45:16 -08:00