026f2e3b (doc: convert git-log to new documentation format, 2025-07-07)
transformed the inline description of `--decorate` options to a
description list:
We also transform inline descriptions of possible values of option
--decorate into a list, which is more readable and extensible.
But a source code block was used instead of an open block.
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
The newly introduced git-history(1) command provides functionality to
easily edit commit history while also rebasing dependent branches. The
functionality exposed by this command is still somewhat limited though.
One common use case when editing commit history that is not yet covered
is fixing up a specific commit. Introduce a new subcommand that allows
the user to do exactly that by performing a three-way merge into the
target's commit tree, using HEAD's tree as the merge base. The flow is
thus essentially:
Like with the other commands, this will automatically rebase dependent
branches, as well. Unlike the other commands though:
- The command does not work in a bare repository as it interacts with
the index.
- The command may run into merge conflicts. If so, the command will
simply abort.
Especially the second item limits the usefulness of this command a bit.
But there are plans to introduce first-class conflicts into Git, which
will help use cases like this one.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/history: generalize function to commit trees
The function `commit_tree_with_edited_message_ext()` can be used to
commit a tree with a specific list of parents with an edited commit
message. This function is useful outside of editing the commit message
though, as it also performs the plumbing to extract the original commit
message and strip some headers from it.
Refactor the function to receive a flags field that allows the caller to
control whether or not the commit message should be edited, or whether
it should be retained as-is. This will be used in a subsequent commit.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
replay: allow callers to control what happens with empty commits
When replaying commits it may happen that some of the commits become
empty relative to their parent. Such commits are for now automatically
dropped by the replay subsystem without much control from the user.
Introduce a new enum that allows the caller to drop, keep or abort in
this case.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
When an attacker can convince a user to clone a crafted repository
that contains an embedded bare repository with malicious hooks, any Git
command the user runs after entering that subdirectory will discover
the bare repository and execute the hooks. The user does not even need
to run a Git command explicitly: many shell prompts run `git status`
in the background to display branch and dirty state information, and
`git status` in turn may invoke the fsmonitor hook if so configured,
making the user vulnerable the moment they `cd` into the directory. The
`safe.bareRepository` configuration variable (introduced in 8959555cee7e
(setup_git_directory(): add an owner check for the top-level directory,
2022-03-02)) already provides protection against this attack vector by
allowing users to set it to "explicit", but the default remained "all"
for backwards compatibility.
Since Git 3.0 is the natural point to change defaults to safer
values, flip the default from "all" to "explicit" when built with
`WITH_BREAKING_CHANGES`. This means Git will refuse to work with bare
repositories that are discovered implicitly by walking up the directory
tree. Bare repositories specified via `--git-dir` or `GIT_DIR` continue
to work, and directories that look like `.git`, worktrees, or submodule
directories are unaffected (the existing `is_implicit_bare_repo()`
whitelist handles those cases).
Users who rely on implicit bare repository discovery can restore the
previous behavior by setting `safe.bareRepository=all` in their global
or system configuration.
The test for the "safe.bareRepository in the repository" scenario
needed a more involved fix: it writes a `safe.bareRepository=all`
entry into the bare repository's own config to verify that repo-local
config does not override the protected (global) setting. Previously,
`test_config -C` was used to write that entry, but its cleanup runs `git
-C <bare-repo> config --unset`, which itself fails when the default is
"explicit" and the global config has already been cleaned up. Switching
to direct git config --file access avoids going through repository
discovery entirely.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
status tests: filter `.gitconfig` from status output
Since test-lib.sh creates `$HOME/.gitconfig` when
`WITH_BREAKING_CHANGES` is in effect, the file appears in `git
status` output as either untracked (`?? .gitconfig`) or ignored
(`!! .gitconfig` / `! .gitconfig`, depending on porcelain version),
because the `.git/info/exclude` entry causes git to treat it as an
ignored file rather than hiding it entirely.
In t7061 and t7521, which are pervasively affected, introduce a
`filter_gitconfig` helper that strips all status-prefix variants of
`.gitconfig` from the output before comparison. In the remaining
scripts (t7060, t7064, t7508), apply targeted adjustments.
Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
ls-files tests: filter `.gitconfig` from `--others` output
The global `safe.bareRepository=all` setting in test-lib.sh is
written to `$HOME/.gitconfig`, which unfortunately lives inside the
test repository's working tree. The `.git/info/exclude` entry added
alongside it handles most commands, but `git ls-files --others`
without `--exclude-standard` does not consult `info/exclude` at
all, so the file appears in the output.
Ideally, each test that accesses a bare repository would simply
specify `--git-dir` or `GIT_DIR` explicitly, which would require no
global config and produce no side effects in the working tree. As
that approach was not taken, filter `.gitconfig` from the output
before comparing against expected results. In t7104, the test
already uses `--exclude-standard`, so it suffices to switch from
the bare `git ls-files -o` to `git ls-files -o --exclude-standard`
which respects the `info/exclude` entry; the other tests
deliberately omit `--exclude-standard` because their purpose is to
verify unfiltered `--others` output.
Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
One test in t5601 overwrites `$HOME/.gitconfig` with an `includeIf`
configuration snippet and removes the file in its cleanup. This
destroys the `safe.bareRepository=all` entry that test-lib.sh
writes when `WITH_BREAKING_CHANGES` is in effect, causing later
tests that use `git -C <bare-repo> config` to fail with "not in a
git directory".
Back up `.gitconfig` before overwriting and restore it in the
cleanup, so the global config survives into subsequent tests.
Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t1305: use `--git-dir=.` for bare repo in include cycle test
Earlier tests in t1305 overwrite `$HOME/.gitconfig` with their own
content as part of testing config includes. This clobbers the
`safe.bareRepository=all` entry that test-lib.sh writes when
`WITH_BREAKING_CHANGES` is in effect, causing `git -C cycle config`
to fail with "not in a git directory" when it tries to access the
bare repository created by `git init --bare cycle`.
Use `--git-dir=.` to access the bare repo explicitly, avoiding the
dependency on global config for repository discovery.
Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t1300: remove global config settings injected by test-lib.sh
Since test-lib.sh now writes `safe.bareRepository=all` to the global
config when `WITH_BREAKING_CHANGES` is in effect, that entry shows
up in `git config --list` output. Tests in t1300 that expect exact
config contents then fail because of this unexpected extra line.
Unlike the working-tree contamination fixed in the preceding
commits, this is not about the file's existence but about its
content leaking into test expectations. Since t1300 does not use
bare repositories, simply remove the injected setting in a
preparatory step.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Assisted-by: Claude Opus 4.6 Signed-off-by: Junio C Hamano <gitster@pobox.com>
t7900: do not let `$HOME/.gitconfig` interfere with XDG tests
The XDG config tests for `git maintenance register/unregister`
create a fresh `$XDG_CONFIG_HOME/git/config` and expect git to use
that location. However, if `$HOME/.gitconfig` exists (which may
happen when test-lib.sh writes global config, e.g. to set
`safe.bareRepository`), git prefers `$HOME/.gitconfig` over the XDG
location, and the `maintenance.repo` entry ends up in the wrong
file.
This is an inherent consequence of setting global config in
test-lib.sh rather than adjusting individual tests: writing any
entry to `$HOME/.gitconfig` has side effects beyond the intended
setting, because the mere existence of that file changes which
global config location git prefers for all subsequent writes.
Individual per-test adjustments would not have this interaction.
Fix this by overriding `HOME` to a non-existent directory inside the
subshells that test XDG behavior. Since these subshells already
override `XDG_CONFIG_HOME`, they do not need `$HOME/.gitconfig` at
all, and the subshell scoping ensures the original `HOME` is
restored automatically.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
test-lib: allow bare repository access when breaking changes are enabled
A future patch will change the `safe.bareRepository` default from
`all` to `explicit` under `WITH_BREAKING_CHANGES`. At that point,
every test that operates on a bare repository through implicit
discovery would fail, regardless of whether the test is actually
about discovery or about how a specific command behaves once inside
a bare repository.
The maintainer suggested [1] setting `safe.bareRepository=all` in
the test environment's global config whenever `WITH_BREAKING_CHANGES`
is in effect, rather than adjusting each affected test to access
bare repositories explicitly (via `--git-dir`, `GIT_DIR`, or
similar). This means the test suite continues to exercise only the
historical default behavior even after the user-facing default
changes, relying on a small number of dedicated tests in t0035 to
validate the new, stricter default.
Since `$HOME` points at the trash directory (which doubles as the
test repository's working tree), writing to `$HOME/.gitconfig` also
creates a file inside the working tree. Exclude it via
`.git/info/exclude` to limit the fallout, though this does not
help tests that use `git ls-files --others` without
`--exclude-standard` or `git status --ignored`; those are addressed
by subsequent commits.
The `.git/info/exclude` write is guarded by `test -d .git/info`
rather than using `mkdir -p`, because some tests (e.g. t0008)
expect to create `.git/info/` themselves and would fail with
Patrick Steinhardt's `set -e` preparation (ps/test-set-e-clean) if
the directory already existed. For tests using `TEST_NO_CREATE_REPO`
(where no `.git/` exists at all), the guard also handles that case.
Original-patch-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
We need to adjust the test in t7810 as well. The file it uses has the
following five lines; I add a line highlighting the matches and a ruler
at the bottom here, to make it easier to see that the second "mmap"
indeed starts at column 14:
foo mmap bar
foo_mmap bar
foo_mmap bar mmap
foo mmap bar_mmap
foo_mmap bar mmap baz
==== ==== 123456789123456789 1
Reported-by: Brandon Chinn <brandonchinn178@gmail.com> Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Historically, config entries like alias.foo.bar expanded the alias
"foo.bar". The subsection-based alias syntax introduced in ac1f12a9de (alias: support non-alphanumeric names via subsection
syntax, 2026-02-18) broke that behavior by treating such entries as
if they were subsection syntax.
Restore support for the old dotted form by falling back to the full
name when the final key is not "command". Add tests covering execution
and help output for simple dotted aliases.
Reported-by: Michael Grossfeld <michael.grossfeld@amd.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Jonatan Holmgren <jonatan@jontes.page> Signed-off-by: Junio C Hamano <gitster@pobox.com>
The goal of that commit was to avoid zombie child processes hanging
around when the parent git process is killed. But it doesn't quite work
when the child command is run by the shell:
1. If there is a shell, then we kill and wait for the shell, not the
process spawned by the shell. And so the child process, even if it
eventually exits, will hang around as a zombie forever. And this is
true of most (all?) shells: bash, dash, etc.
So we are not really accomplishing our goal in the first place.
2. Not all shells will exit immediately upon receiving a signal. In
particular, mksh will wait for its children to exit (but not
actually propagate the signal to them!) leaving us with a potential
deadlock: git is wait()ing on mksh, which is wait()ing on a child
process, but that child process is waiting on git to produce more
input (or EOF) over a pipe.
You can see several examples of this deadlock in the test suite,
for example by running:
make SHELL_PATH=/bin/mksh
cd t
./t5702-protocol-v2.sh
Because this is a regression for mksh users, and because we did not
achieve our goal even with other shells, let's revert the commit for
now. If there is a more clever way of doing the same thing, we can
consider applying it separately on top (or do nothing and just accept
the zombies and rely on PID 1 to reap them).
Reported-by: Jan Palus <jpalus@fastmail.com> Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
SZEDER Gábor [Tue, 21 Apr 2026 19:21:32 +0000 (21:21 +0200)]
t6112: avoid tilde expansion
e987df5fe6 (list-objects-filter: implement composite filters,
2019-06-27) introduced a test to "t6112-rev-list-filters-objects.sh"
that checks the output of a Git command with the following commands:
grep ~$omitted_1 actual &&
grep ~$omitted_2 actual &&
grep ~$omitted_3 actual &&
Since the leading tilde in the pattern is not quoted/escaped, it is
subject to tilde expansion. So if the system has a user whose
username happens to be "$omitted_1", then "grep" would look for that
user's home directory.
Quote those words starting with a tilde to avoid this.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Back in commit a562d90a350d (merge-ort: fix failing merges in special
corner case, 2025-11-03), we hit a rename assertion due to a trivial
directory resolution affecting the parent of a cached rename. Since
the path didn't need to be considered, we side-stepped it with
if (!newinfo)
continue;
in process_renames(). We have since run into a case in production
where a trivial resolution of a file affects the direct target of a
cached rename rather than a parent directory of it. Add a testcase
demonstrating this additional case.
Now, if we were to follow the lead of commit a562d90a350d, we could
resolve this alternate case with an extra condition on the above if:
if (!newinfo || newinfo->merged.clean)
continue;
However, if we had done that earlier, we would have made 979ee83e8a90
(merge-ort: fix corner case recursive submodule/directory conflict
handling, 2025-12-29) harder to find and fix, and this particular
position for this condition isn't actually at the root of the issue
but downstream from it.
Instead, let's rip out this if-check from a562d90a350d and put in an
alternative that more directly addresses trivially resolved paths that
happen to be cached renames or parent directories thereof, which is a
better fix for the original testcase and which also solves the newly
added testcase as well.
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
We have recently merged a patch series that had a simple misspelling of
`test_expect_success`. Instead of making our tests fail though, this
typo went completely undetected and all of our tests passed, which is of
course unfortunate. This is a more general issue with our test suite:
all commands that run outside of a specific test case can fail, and if
we don't explicitly check for such failure then this failure will be
silently ignored.
Improve the status quo by enabling the errexit option so that any such
unchecked failures will cause us to abort immediately.
Note that for now, we only enable this option for Bash 5 and newer. This
is because other shells have wildly different behaviour, and older
versions of Bash (especially on macOS) are buggy. The list of enabled
shells may be extended going forward.
Helped-by: Jeff King <peff@peff.net> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
In t9902 we're using the `read` builtin to read some values into a
variable. This is done by using `-d ""`, which cause us to read until
the end of the heredoc. As the read is terminated by EOF, the command
will end up returning a non-zero error code. This hasn't been an issue
until now as we didn't run with `set -e`, but that'll change in a
subsequent commit.
Prepare for this change by not using read at all, as we can simply store
the multi-line value directly.
Suggested-by: SZEDER Gábor <szeder.dev@gmail.com> Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
In `test_bisection_diff ()` we use `expr` to perform some math. This
command has some gotchas though in that it will only return success when
the result is neither null nor zero. In some of our cases though it
actually _is_ zero, and that will cause the expressions to fail once we
enable `set -e`.
Prepare for this change by instead using `$(( ))`, which doesn't have
the same issue. While at it, modernize the function a tiny bit.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t1301: don't fail in case setfacl(1) doesn't exist or fails
In t1301 we're trying to remove any potentially-existing default ACLs
that might exist on the transh directory by executing setfacl(1).
According to 8ed0a740dd (t1301-shared-repo.sh: don't let a default ACL
interfere with the test, 2008-10-16), this is done because we play
around with permissions and umasks in this test suite.
The setfacl(1) binary may not exist on some systems though, even though
tests ultimately still pass. This doesn't matter currently, but will
cause the test to fail once we start running with `set -e`. Silence such
failures by ignoring failures here.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t0008: silence error in subshell when using `grep -v`
In t0008 we use `grep -v` in a subshell, but expect that this command
will sometimes not match anything. This would cause grep(1) to return an
error code, but given that we don't run with `set -e` we swallow this
error.
We're about to enable `set -e`. Prepare for this by ignoring any errors.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t: prepare `test_when_finished ()`/`test_atexit()` for `set -e`
Both `test_when_finished ()` and `test_atexit ()` build up a chain of
cleanup commands by prepending each new command to the existing cleanup
string. To preserve the exit code of the test body across cleanup
execution, we append the following logic:
} && (exit "$eval_ret"); eval_ret=$?; ...
The intent of this is to run the cleanup block and then unconditionally
restore `eval_ret`. The original behaviour of this is is:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ && taken -> (exit 0) -> eval_ret=0 |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ && taken -> (exit 1) -> eval_ret=1 |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | && not taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
This logic will start to fail once we enable `set -e`. When `$eval_ret`
is non-zero, the subshell we create will fail, and with `set -e` we'll
thus bail out without evaluating the logic after the semicolon.
Fix this issue by instead using `|| eval_ret=\$?; ...`. Besides being
a bit simpler, it also retains the original behaviour:
+------------------+---------+------------------------------------+
|test body │ cleanup │ old behaviour │
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│pass (eval_ret=0) | fail │ || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | pass │ || not taken -> eval_ret unchanged |
+------------------+---------+------------------------------------+
│fail (eval_ret=1) | fail | || taken -> eval_ret=$? |
+------------------+---------+------------------------------------+
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t: prepare execution of potentially failing commands for `set -e`
Several of our tests verify whether a certain binary can be executed,
potentially skipping tests in case we cannot, for example because the
binary doesn't exist. In those cases we often run the binary outside of
any conditionally.
This will start to fail once we enable `set -e`, as that will cause us
to bail out the test immediately. Improve these tests by executing them
inside of a conditional instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t: prepare conditional test execution for `set -e`
We have some test in our test suite where we use the pattern of
`test ... && test_expect_succeess` to conditionally execute a test. The
problem is that when we decide to not execute the test, we'll indeed
skip the test, but the overall statement will also be unsuccessful. This
will become a problem once we enable `set -e`.
Prepare for this future by turning this into a proper conditional, which
is also a bit easier to read overall.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t: prepare `git config --unset` calls for `set -e`
We have a couple of calls to `git config --unset` that ultimately end up
as no-ops as the configuration variables aren't set (anymore) in the
first place. These calls are mostly intended to recover unconditionally
from tests that may have executed only partially, but they'll ultimately
fail during a normal test run.
This hasn't been a problem until now as we aren't running tests with
`set -e`. This is about to change though, so let's silence the case
where we cannot unset the config keys.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
We have a couple of calls to `stop_git_daemon ()` outside of specific
test cases that will kill a backgrounded git-daemon(1) process and
expect the process with a specific error code. While these function
calls do end up killing git-daemon(1), the error handling we have in
those contexts is basically ineffective. So while we expect the process
to exit with a specific error code, we will just continue with any error
in case it doesn't.
This will change once we enable `set -e` in a subsequent commit. There's
two issues though that will make this _always_ fail:
- Our call to `wait` is expected to fail, but because it's not part of
a condition it will cause us to bail out immediately with `set -e`.
- We try to kill git-daemon(1) a second time via the pidfile. We can
generally expect that this is the same PID though as we had in the
"GIT_DAEMON_PID" environment variable, and thus it's more likely
than not that we have already killed it, and the call to kill will
fail.
Prepare for this change by handling the failure of `wait` with `||` and
by silencing failures of the second call to `kill`.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
The helper function `test_must_fail ()` executes a specific Git command
that may or may not fail in a specific way. This is done by executing
the command in question and then comparing its exit code against a set
of conditions.
This works, but once we run our test suite with `set -e` we may bail out
of `test_must_fail ()` early in case the command actually fails, even
though we expect it to fail. Prepare for this change by handling the
failed case with `||`.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t: prepare `test_match_signal ()` calls for `set -e`
We have a couple of calls to `test_match_signal ()` where we execute a
Git command and expect it to die with a specific signal. These calls
will essentially execute the process in a subshell via `foo; echo $?`,
but as we expect `foo` to fail this will cause the overall subshell to
fail once we `set -e`.
Fix this issue by using `foo && echo 0 || echo $?` instead.
Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Several tests in t7004 use the 'test$(git ...) = ...' or the '! (git ...)'
subshell pattern. This swallows git's exit code. If git crashes
(e.g. segmentation fault) the crash would go undetected, and the test
would fail due to a mismatch or an inverted exit code.
Modernize these tests by directly writing output to files(actual) and
verifying them with 'test_cmp' or 'test_grep'. Replace subshell
negations with 'test_must_fail'. This way, if git crashes, the test
fails immediately and clearly instead of hiding the error behind a
string mismatch.
Signed-off-by: Siddharth Shrimali <r.siddharth.shrimali@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
The tests for 'Multiple -l or --list options' and 'trying to delete
tags without params', hardcodes that exactly one or two specific tags
('myhead', 'mytag') exist in the repository.
If other tests are added, modified, or removed earlier in the script,
this expected global state will change, resulting in these tests to fail
for completely unrelated reasons.
Instead of hardcoding the expected tags, dynamically grab the state
of the repository before running the commands under test ('git tag -l'
and 'git tag -d'), and verify that the output matches or remains
unchanged afterward. This keeps the tests independent from the script's
overall state.
Signed-off-by: Siddharth Shrimali <r.siddharth.shrimali@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t7004: drop hardcoded tag count for state verification
The test 'trying to create a tag with a non-valid name should fail',
checked that exactly one tag existed in the repository before and after
attempting to create invalid tags.
As pointed out by Junio, this makes the test brittle by relying on a
specific global tag count. If future tests are added or removed before
this test, the expected state changes and this test would break for
completely unrelated reasons.
Modernize the test by taking a snapshot of the existing tags before the
failure attempts and comparing it to a snapshot taken after.
This provides a "belt-and-suspenders" approach: we verify that
'git tag' both exits with the expected error code and leaves the
repository state untouched, without being brittle to the specific
number of tags present.
This replaces the hardcoded 'test_line_count = 1' checks with 'test_cmp'
to ensure the tag list remains identical.
Suggested-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Siddharth Shrimali <r.siddharth.shrimali@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff: fix out-of-bounds reads and NULL deref in diffstat UTF-8 truncation
f85b49f3d4a (diff: improve scaling of filenames in diffstat to handle
UTF-8 chars, 2026-01-16) introduced a loop in show_stats() that calls
utf8_width() repeatedly to skip leading characters until the displayed
width fits. However, utf8_width() can return problematic values:
- For invalid UTF-8 sequences, pick_one_utf8_char() sets the name
pointer to NULL and utf8_width() returns 0. Since name_len does
not change, the loop iterates once more and pick_one_utf8_char()
dereferences the NULL pointer, crashing.
- For control characters, utf8_width() returns -1, so name_len
grows when it is expected to shrink. This can cause the loop to
consume more characters than the string contains, reading past
the trailing NUL.
By default, fill_print_name() will C-quote filenames which escapes
control characters and invalid bytes to printable text. That avoids
this bug from being triggered; however, with core.quotePath=false,
most characters are no longer escaped (though some control characters
still are) and raw bytes can reach this code.
Add tests exercising both failure modes with core.quotePath=false and
a narrow --stat-name-width to force truncation: one with a bare 0xC0
byte (invalid UTF-8 lead byte, triggers NULL deref) and one with
several C1 control characters (repeats of 0xC2 0x9F, causing
the loop to read past the end of the string). The second test
reliably catches the out-of-bounds read when run under ASan, though
it may pass silently without sanitizers.
Fix both issues by introducing utf8_ish_width(), a thin wrapper
around utf8_width() that guarantees the pointer always advances and
the returned width is never negative:
- On invalid UTF-8 it restores the pointer, advances by one byte,
and returns width 1 (matching the strlen()-based fallback used
by utf8_strwidth()).
- On a control character it returns 0 (matching utf8_strnwidth()
which skips them).
Also add a "&& *name" guard to the while-loop condition so it
terminates at end-of-string even when utf8_strwidth()'s strlen()
fallback causes name_len to exceed the sum of per-character widths.
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Rob McDonald [Tue, 14 Apr 2026 16:34:03 +0000 (09:34 -0700)]
gitk: add horizontal scrollbar to the commit list pane
When many branches and tags decorate the same commit, the ref labels
push the commit description far to the right, often out of the visible
area of the left pane. The canvas widget already tracked the maximum
x extent via canvxmax and updated its scrollregion accordingly, but
there was no scrollbar wired up to let the user reach that content.
Add a horizontal scrollbar (.tf.histframe.cxsb) below the three-pane
history area, connected to the left canvas (canv) via its xscrollcommand
and xview.
No behaviour is changed for the author (canv2) or date (canv3) panes;
they continue to scroll only vertically in lock-step as before.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Rob McDonald <rob.a.mcdonald@gmail.com>
6cc6d1b4c699 (Documentation: update add --force option + ignore=all
config, 2026-02-06) added text describing both the ignore=none and
ignore=all behaviors. The former had minor formatting and grammatical
errors, while the latter was a bit garbled. I have tried to tweak the
wording on the latter to make it read as I think was intended, and fixed
the minor grammatical issues with both as well.
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
doc: fix self-referential config in sendemail.smtpSSLClientKey
a8215a205141 (send-email: add client certificate options, 2026-03-02)
added documentation for sendemail.smtpSSLClientKey that says it works
"in conjunction with `sendemail.smtpSSLClientKey`" -- referring to
itself. It appears that `sendemail.smtpSSLClientCert` was the intended
reference; fix it.
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Thu, 16 Apr 2026 20:06:59 +0000 (16:06 -0400)]
MIDX: revert the default version to v1
We introduced midx version 2 in b2ec8e90c2 (midx: do not require packs
to be sorted in lexicographic order, 2026-02-24) and now write it by
default. The rationale was that older versions should ignore the v2 midx
and fall back to using the packs (just like we do for other midx
errors). Unfortunately this is not the case, as we have a hard die()
when we see an unknown midx version.
As a result, writing a midx with Git 2.54-rc2 puts the repository into a
state that is unusable with Git 2.53. And this midx write may happen
behind the scenes as part of normal operations, like fetch.
Let's switch back to writing v1 by default to avoid regressing the case
where multiple versions of Git are used on the same repository.
There is one gotcha, though: the v2 format is required for some new
features, like midx compaction, and running "git multi-pack-index
compact" will complain when asked to write a v1 index. The user must set
midx.version to "2" to make the feature work.
So instead of always using v1, we'll base the default on whether the
requested feature requires v2. That does mean that running midx
compaction will create a repository that can't be read by older versions
of Git. But we never do that by default; only people experimenting with
the new feature will be affected.
We have to adjust the test expectation in t5319, since it will now
generate v1 files. And our "auto-select v2" is covered by the tests in
t5335, which continue to check that compaction works without having to
set midx.version manually (and also explicitly check that asking for v1
with compaction reports the problem).
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
t5563: add tests for http.emptyAuth with Negotiate
Add tests exercising the interaction between http.emptyAuth and
servers that advertise Negotiate (SPNEGO) authentication.
Verify that auto mode gives Negotiate a chance via empty auth
(resulting in two 401 responses before falling through to
credential_fill with Basic credentials), and that false mode
strips Negotiate immediately (only one 401 response).
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
http: attempt Negotiate auth in http.emptyAuth=auto mode
When a server advertises Negotiate (SPNEGO) authentication, the
"auto" mode of http.emptyAuth should detect this as an "exotic"
method and proactively send empty credentials, allowing libcurl to
use the system Kerberos ticket without prompting the user.
However, two features interact to prevent this from working:
The Negotiate-stripping logic, introduced in 4dbe66464b
(remote-curl: fall back to Basic auth if Negotiate fails,
2015-01-08), removes CURLAUTH_GSSNEGOTIATE from the allowed
methods on the first 401 response. The empty-auth auto-detection,
introduced in 40a18fc77c (http: add an "auto" mode for
http.emptyauth, 2017-02-25), then checks the remaining methods
for anything "exotic" -- but Negotiate has already been removed,
so auto mode never activates for servers whose only non-Basic/Digest
method is Negotiate (e.g., Apache with mod_auth_kerb offering
Basic + Negotiate).
Fix this by delaying the Negotiate stripping in auto mode: on the
first 401, keep Negotiate in the allowed methods so that auto mode
can detect it and retry with empty credentials. If that attempt
fails (no valid Kerberos ticket), strip Negotiate on the second 401
and fall through to credential_fill() as usual.
To support this, also teach http_reauth_prepare() to skip
credential_fill() when empty auth is about to be attempted, since
filling real credentials would bypass the empty-auth mechanism.
The true and false modes are unchanged: true sends empty credentials
on the very first request (before any 401), and false never sends
them.
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
http: extract http_reauth_prepare() from retry paths
All three HTTP retry paths (http_request_recoverable, post_rpc,
probe_rpc) call credential_fill() directly when handling
HTTP_REAUTH. Extract this into a helper function so that a
subsequent commit can add pre-fill logic (such as attempting
empty-auth before prompting) in one place.
No functional change.
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
- correct translation of pathspec msgs
Corrects cases where the “pathspec” is translated as if it was a
path
- correct translation of refspec msgs
Corrects cases where the “refspec” were not consistently translated
- correct translation of credential msgs
Corrects cases where the “credential” were not correctly translated
Signed-off-by: Stefan Björnelund <stefan.bjornelund.gnome@gmail.com> Modified-by: Peter Krefting <peter@softwolves.pp.se>
cat-file: add mailmap subcommand to --batch-command
git-cat-file(1)'s --batch-command works with the --use-mailmap option,
but this option needs to be set when the process is created. This means
we cannot change this option mid-operation.
At GitLab, Gitaly keeps interacting with a long-lived git-cat-file
process and it would be useful if --batch-command supported toggling
mailmap dynamically on an existing process.
Add a `mailmap` subcommand to --batch-command that takes a boolean
argument (usual ways you can specify a boolean value like 'yes', 'true',
etc., are supported). Mailmap data is loaded lazily and kept in memory,
while a helper centralizes the one-time load path used both at startup
and from the batch-command handler.
Extend tests to cover runtime toggling, startup option interactions
(`--mailmap`/`--no-mailmap`), accepted boolean forms, and invalid values.
Signed-off-by: Siddharth Asthana <siddharthasthana31@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Commit 302aff09223f (backfill: accept revision arguments, 2026-03-26) added
support for accepting revision arguments to backfill. This allows users
to do things like
git backfill --remotes ^v2.3.0
and then run many commands without triggering on-demand downloads of
blobs. However, if they have topics based on v2.3.0, they will likely
still trigger on-demand downloads. Consider, for example, the command
git log -p v2.3.0..topic
This would still trigger on-demand blob loadings after the backfill
command above, because the commit(s) with A as a parent will need to
diff against the blobs in A. In fact, multiple commands need blobs from
the lower boundary of the revision range:
* git log -p A..B # After backfill A..B
* git replay --onto TARGET A..B # After backfill TARGET^! A..B
* git checkout A && git merge B # After backfill A...B
Add an extra --[no-]include-edges flag to allow grabbing blobs from
edge commits. Since the point of backfill is to prevent on-demand blob
loading and these are common commands, default to --include-edges.
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
backfill: document acceptance of revision-range in more standard manner
302aff09223f (backfill: accept revision arguments, 2026-03-26) added
support for passing revision arguments to 'git backfill' but documented
them only with a prose sentence:
You may also specify the commit limiting options from
git-rev-list(1).
No other command that accepts revision arguments documents them this
way. Commands like log, shortlog, and replay define a formal
<revision-range> entry and include rev-list-options.adoc. Commands like
bundle, fast-export, and filter-branch, which pass arguments through to
the revision machinery without including the full options file, still
define a formal <git-rev-list-args> entry explaining what is accepted.
Add a formal <revision-range> entry in the synopsis and OPTIONS section,
following the convention used by other commands, and mention that
commit-limiting options from git-rev-list(1) are also accepted.
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
backfill: reject rev-list arguments that do not make sense
Some rev-list options accepted by setup_revisions() are silently
ignored or actively counterproductive when used with 'git backfill',
because the path-walk API has its own tree-walking logic that bypasses
the mechanisms these options rely on:
* -S/-G (pickaxe) and --diff-filter work by computing per-commit
diffs in get_revision_1() and filtering commits whose diffs don't
match. Since backfill's goal is to download all blobs reachable
from commits in the range, filtering out commits based on diff
content would silently skip blobs -- the opposite of what users
want.
* --follow disables path pruning (revs->prune) and only makes
sense for tracking a single file through renames in log output.
It has no useful interaction with backfill.
* -L (line-log) computes line-level diffs to track the evolution
of a function or line range. Like pickaxe, it filters commits
based on diff content, which would cause blobs to be silently
skipped.
* --diff-merges controls how merge commit diffs are displayed.
The path-walk API walks trees directly and never computes
per-commit diffs, so this option would be silently ignored.
* --filter (object filtering, e.g. --filter=blob:none) is used by
the list-objects traversal but is completely ignored by the
path-walk API, so it would silently do nothing.
Rather than letting users think these options are being honored,
reject them with a clear error message.
Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Originally-authored-by: dependabot[bot] <support@github.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:37 +0000 (13:27 +0000)]
fsmonitor: convert shown khash to strset in do_handle_client
Replace the khash-based string set used for deduplicating pathnames
in do_handle_client() with a strset, which provides a cleaner
interface for the same purpose.
Since the paths are interned strings from the batch data, use
strdup_strings=0 to avoid unnecessary copies.
Suggested-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:36 +0000 (13:27 +0000)]
fsmonitor: add tests for Linux
Add a smoke test that verifies the filesystem actually delivers
inotify events to the daemon. On some configurations (e.g.,
overlayfs with older kernels), inotify watches succeed but events
are never delivered. The daemon cookie wait will time out, but
every subsequent test would fail. Skip the entire test file early
when this is detected.
Add a test that exercises rapid nested directory creation to verify
the daemon correctly handles the EEXIST race between recursive scan
and queued inotify events. When IN_MASK_CREATE is available and a
directory watch is added during recursive registration, the kernel
may also deliver a queued IN_CREATE event for the same directory.
The second inotify_add_watch() returns EEXIST, which must be treated
as harmless. An earlier version of the listener crashed in this
scenario.
Reduce --start-timeout from the default 60 seconds to 10 seconds so
that tests fail promptly when the daemon cannot start.
Harden the test helpers to work in environments without procps
(e.g., Fedora CI): fall back to reading /proc/$pid/stat for the
process group ID when ps is unavailable, guard stop_git() against
an empty pgid, and redirect stderr from kill to /dev/null to avoid
noise when processes have already exited.
Use set -m to enable job control in the submodule-pull test so that
the background git pull gets its own process group, preventing the
shell wait from blocking on the daemon. setsid() in the previous
commit detaches the daemon itself, but the intermediate git pull
process still needs its own process group for the test shell to
manage it correctly.
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:35 +0000 (13:27 +0000)]
fsmonitor: add timeout to daemon stop command
The "fsmonitor--daemon stop" command polls in a loop waiting for the
daemon to exit after sending a "quit" command over IPC. If the daemon
fails to shut down (e.g. it is stuck or wedged), this loop spins
forever.
Add a 30-second timeout so the stop command returns an error instead
of blocking indefinitely.
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:34 +0000 (13:27 +0000)]
fsmonitor: close inherited file descriptors and detach in daemon
When the fsmonitor daemon is spawned as a background process, it may
inherit file descriptors from its parent that it does not need. In
particular, when the test harness or a CI system captures output through
pipes, the daemon can inherit duplicated pipe endpoints. If the daemon
holds these open, the parent process never sees EOF and may appear to
hang.
Set close_fd_above_stderr on the child process at both daemon startup
paths: the explicit "fsmonitor--daemon start" command and the implicit
spawn triggered by fsmonitor-ipc when a client finds no running daemon.
Also suppress stdout and stderr on the implicit spawn path to prevent
the background daemon from writing to the client's terminal.
Additionally, call setsid() when the daemon starts with --detach to
create a new session and process group. This prevents the daemon
from being part of the spawning shell's process group, which could
cause the shell's "wait" to block until the daemon exits.
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:33 +0000 (13:27 +0000)]
run-command: add close_fd_above_stderr option
Add a close_fd_above_stderr flag to struct child_process. When set,
the child closes file descriptors 3 and above between fork and exec
(skipping the child-notifier pipe), capped at sysconf(_SC_OPEN_MAX)
or 4096, whichever is smaller. This prevents the child from
inheriting pipe endpoints or other descriptors from the parent
environment (e.g., the test harness).
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:32 +0000 (13:27 +0000)]
fsmonitor: implement filesystem change listener for Linux
Implement the built-in fsmonitor daemon for Linux using the inotify
API, bringing it to feature parity with the existing Windows and macOS
implementations.
The implementation uses inotify rather than fanotify because fanotify
requires either CAP_SYS_ADMIN or CAP_PERFMON capabilities, making it
unsuitable for an unprivileged user-space daemon. While inotify has
the limitation of requiring a separate watch on every directory (unlike
macOS's FSEvents, which can monitor an entire directory tree with a
single watch), it operates without elevated privileges and provides
the per-file event granularity needed for fsmonitor.
The listener uses inotify_init1(O_NONBLOCK) with a poll loop that
checks for events with a 50-millisecond timeout, keeping the inotify
queue well-drained to minimize the risk of overflows. Bidirectional
hashmaps map between watch descriptors and directory paths for efficient
event resolution. Directory renames are tracked using inotify's cookie
mechanism to correlate IN_MOVED_FROM and IN_MOVED_TO event pairs; a
periodic check detects stale renames where the matching IN_MOVED_TO
never arrived, forcing a resync.
New directory creation triggers recursive watch registration to ensure
all subdirectories are monitored. The IN_MASK_CREATE flag is used
where available to prevent modifying existing watches, with a fallback
for older kernels. When IN_MASK_CREATE is available and
inotify_add_watch returns EEXIST, it means another thread or recursive
scan has already registered the watch, so it is safe to ignore.
Remote filesystem detection uses statfs() to identify network-mounted
filesystems (NFS, CIFS, SMB, FUSE, etc.) via their magic numbers.
Mount point information is read from /proc/mounts and matched against
the statfs f_fsid to get accurate, human-readable filesystem type names
for logging. When the .git directory is on a remote filesystem, the
IPC socket falls back to $HOME or a user-configured directory via the
fsmonitor.socketDir setting.
Based-on-patch-by: Eric DeCosta <edecosta@mathworks.com> Based-on-patch-by: Marziyeh Esipreh <marziyeh.esipreh@gmail.com> Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:31 +0000 (13:27 +0000)]
fsmonitor: rename fsm-settings-darwin.c to fsm-settings-unix.c
The fsmonitor settings logic in fsm-settings-darwin.c is not
Darwin-specific and will be reused by the upcoming Linux
implementation. Rename it to fsm-settings-unix.c to reflect that it
is shared by all Unix platforms.
Update the build files (meson.build and CMakeLists.txt) to use
FSMONITOR_OS_SETTINGS for fsm-settings, matching the approach already
used for fsm-ipc.
Based-on-patch-by: Eric DeCosta <edecosta@mathworks.com> Based-on-patch-by: Marziyeh Esipreh <marziyeh.esipreh@gmail.com> Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:30 +0000 (13:27 +0000)]
fsmonitor: rename fsm-ipc-darwin.c to fsm-ipc-unix.c
The fsmonitor IPC path logic in fsm-ipc-darwin.c is not
Darwin-specific and will be reused by the upcoming Linux
implementation. Rename it to fsm-ipc-unix.c to reflect that it
is shared by all Unix platforms.
Introduce FSMONITOR_OS_SETTINGS (set to "unix" for non-Windows, "win32"
for Windows) as a separate variable from FSMONITOR_DAEMON_BACKEND so
that the build files can distinguish between platform-specific files
(listen, health, path-utils) and shared Unix files (ipc, settings).
Move fsm-ipc to the FSMONITOR_OS_SETTINGS section in the Makefile, and
switch fsm-path-utils to use FSMONITOR_DAEMON_BACKEND since path-utils
is platform-specific (there will be separate darwin and linux versions).
Based-on-patch-by: Eric DeCosta <edecosta@mathworks.com> Based-on-patch-by: Marziyeh Esipreh <marziyeh.esipreh@gmail.com> Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:29 +0000 (13:27 +0000)]
fsmonitor: use pthread_cond_timedwait for cookie wait
The cookie wait in with_lock__wait_for_cookie() uses an infinite
pthread_cond_wait() loop. The existing comment notes the desire
to switch to pthread_cond_timedwait(), but the routine was not
available in git thread-utils.
On certain container or overlay filesystems, inotify watches may
succeed but events are never delivered. In this case the daemon
would hang indefinitely waiting for the cookie event, which in
turn causes the client to hang.
Replace the infinite wait with a one-second timeout using
pthread_cond_timedwait(). If the timeout fires, report an
error and let the client proceed with a trivial (full-scan)
response rather than blocking forever.
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:28 +0000 (13:27 +0000)]
compat/win32: add pthread_cond_timedwait
Add a pthread_cond_timedwait() implementation to the Windows pthread
compatibility layer using SleepConditionVariableCS() with a millisecond
timeout computed from the absolute deadline.
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:27 +0000 (13:27 +0000)]
fsmonitor: fix hashmap memory leak in fsmonitor_run_daemon
The `state.cookies` hashmap is initialized during daemon startup but
never freed during cleanup in the `done:` label of
fsmonitor_run_daemon(). The cookie entries also have names allocated
via strbuf_detach() that must be freed individually.
Iterate the hashmap to free each cookie name, then call
hashmap_clear_and_free() to release the entries and table.
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:26 +0000 (13:27 +0000)]
fsmonitor: fix khash memory leak in do_handle_client
The `shown` kh_str_t was freed with kh_release_str() at a point in
the code only reachable in the non-trivial response path. When the
client receives a trivial response, the code jumps to the `cleanup`
label, skipping the kh_release_str() call entirely and leaking the
hash table.
Fix this by initializing `shown` to NULL and moving the cleanup to the
`cleanup` label using kh_destroy_str(), which is safe to call on NULL.
This ensures the hash table is freed regardless of which code path is
taken.
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Paul Tarjan [Wed, 15 Apr 2026 13:27:25 +0000 (13:27 +0000)]
t9210, t9211: disable GIT_TEST_SPLIT_INDEX for scalar clone tests
index.skipHash (Scalar default) and split-index are incompatible:
the shared index gets a null OID when skipHash skips computing the
hash, and the null OID causes the shared index to not be loaded on
re-read. This triggers a BUG assertion in fsmonitor when the
fsmonitor_dirty bitmap references more entries than the (now empty)
index has.
Disable GIT_TEST_SPLIT_INDEX in the scalar clone tests that hit
this: tests 12, 13, and 22 in t9210 (matching the existing
workaround in test 16), and all of t9211 (every test does scalar
clone).
Signed-off-by: Paul Tarjan <github@paulisageek.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Scott L. Burson [Wed, 15 Apr 2026 02:27:43 +0000 (02:27 +0000)]
userdiff: extend Scheme support to cover other Lisp dialects
Common Lisp has top-level forms, such as 'defun' and 'defmacro', that
are not matched by the current Scheme pattern. Also, it is more
common in CL, when defining user macros intended as top-level forms,
to prefix their names with "def" instead of "define"; such forms are
also not matched. And some top-level forms don't even begin with
"def".
On the other hand, it is an established formatting convention in the
Lisp community that only top-level forms start at the left margin. So
matching any unindented line starting with an open parenthesis is an
acceptable heuristic; false positives will be rare.
However, there are also cases where notionally top-level forms are
grouped together within some containing form. At least in the Common
Lisp community, it is conventional to indent these by two spaces, or
sometimes one. But matching just an open parenthesis indented by two
spaces would be too broad; so the pattern added by this commit
requires an indented form to start with "(def". It is believed that
this strikes a good balance between potential false positives and
false negatives.
Signed-off-by: Scott L. Burson <Scott@sympoiesis.com> Acked-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Johannes Sixt [Wed, 15 Apr 2026 02:27:42 +0000 (02:27 +0000)]
userdiff: tighten word-diff test case of the scheme driver
The scheme driver separates identifiers only at parentheses of all
sorts and whitespace, except that vertical bars act as brackets that
enclose an identifier.
The test case attempts to demonstrate the vertical bars with a change
from 'some-text' to '|a greeting|'. However, this misses the goal
because the same word coloring would be applied if '|a greeting|'
were parsed as two words.
Have an identifier between vertical bars with a space in both the pre-
and the post-image and change only one side of the space to show that
the single word exists between the vertical bars.
Also add cases that change parentheses of all kinds in a sequence of
parentheses to show that they are their own word each.
Signed-off-by: Johannes Sixt <j6t@kdbg.org> Signed-off-by: Scott L. Burson <Scott@sympoiesis.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Sat, 11 Apr 2026 21:55:18 +0000 (17:55 -0400)]
gitglossary: fix indentation of sub-lists
The glossary entry is a list of terms and their definitions, so
multi-paragraph definitions need "+" continuation lines to indicate
that they are part of a single entry.
When an entry contains a sub-list (say, a bulleted list), the final "+"
may become ambiguous: is it connecting the next paragraph to the final
entry of the sub-list, or to the original list of definition paragraphs?
Asciidoc generally connects it to the former, even when we mean the
latter, and you end up with the next paragraph indented incorrectly,
like this:
glob
...defines glob...
Two consecutive asterisks ("**") in patterns matched
against full pathname may have special meaning:
- ...some special meaning of **...
- ...another special meaning of **...
- Other consecutive asterisks are considered invalid.
Glob magic is incompatible with literal magic.
That final "Glob magic is incompatible" paragraph is in the wrong spot.
It should be at the same level as "Two consecutive asterisks", as it is
not part of the final "Other consecutive asterisks" bullet point.
The same problem appears in several other spots in the glossary.
Usually we'd fix this by using "--" markers, which put the sub-list into
its own block. But there's a catch: in some of these spots we are
already in an open block, and nesting open blocks is a problem. It seems
to work for me using Asciidoc 10.2.1, but Asciidoctor 2.0.26 makes a
mess of it (our intent to open a new block seems to close the old one).
Fortunately there's a work-around: when using a "+" list-continuation,
the number of empty lines above the continuation indicates which level
of parent list to continue. So by adding an empty line after our
unordered list (before the "+"), we should be able to continue the
definition list item.
But asciidoc being asciidoc, of course that is not the end of the story.
That technique works fine for the "glob" and "attr" lists in this patch,
but under the "refs" item it works for only 1 of the 2 lists! I can't
figure out why, and this may be an asciidoctor bug. But we can work
around it by using "--" open-block markers here, since we're not
already in an open block.
So using the extra blank line for the first two instances, and "--"
markers for the second two, this patch produces identical output from
"doc-diff HEAD^ HEAD" for both --asciidoctor and --asciidoc modes.
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano [Mon, 13 Apr 2026 18:24:44 +0000 (11:24 -0700)]
CI: bump actions/checkout from 4 to 5 for rust-analysis job
GitHub Actions started complaining about use of Node.js 20 and I was
wondering why only one job uses actions/checkout@v4, while everybody
else already uses actions/checkout@v5.
It turns out that it is caused by a semantic mismerge between e75cd059 (ci: check formatting of our Rust code, 2025-10-15) that
added a new use of actions/checkout@v4 that happened very close to
another change 63541ed9 (build(deps): bump actions/checkout from 4
to 5, 2025-10-16) that updated all uses of actions/checkout@v4 to
use vactions/checkout@v5.
Update the leftover and the last use of actions/checkout@v4 to use
actions/checkout@v5 to help ourselves to move away from Node.js 20.
I claimed in 3c18135b (doc: am: say that --message-id adds a trailer,
2026-02-09) that `git am --message-id` adds a Git trailer. But that
isn’t the case; for the case of a commit message with a subject, body,
and no trailer block:
It does work for two other cases though, namely subject-only and with an
existing trailer block.
This is at best an inconsistency and arguably a bug, but we’re at the
trailing end of the release cycle now. So reverting the doc is safer
than making msg-id act as a trailer, for now.
Revert this hunk from commit 3c18135b except the only useful
change (“Also use inline-verbatim for `Message-ID`”).
Signed-off-by: Kristoffer Haugsbakk <code@khaugsbakk.name> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano [Fri, 10 Apr 2026 23:47:35 +0000 (16:47 -0700)]
Merge branch 'jc/no-writev-does-not-work'
We used writev() in limited code paths and supplied emulation for
platforms without working writev(), but the emulation was too
faithful to the spec to make the result useless to send even 64kB;
revert the topic and plan to restart the effort later.
* jc/no-writev-does-not-work:
Revert "compat/posix: introduce writev(3p) wrapper"
Revert "wrapper: introduce writev(3p) wrappers"
Revert "sideband: use writev(3p) to send pktlines"
Revert "cmake: use writev(3p) wrapper as needed"
Junio C Hamano [Fri, 10 Apr 2026 14:52:50 +0000 (07:52 -0700)]
rust: we are way beyond 2.53
Earlier we timelined that we'd tune our build procedures to build
with Rust by default in Git 2.53, but we are already in prerelease
freeze for 2.54 now. Update the BreakingChanges document to delay
it until Git 2.55 (slated for the end of June 2026).
Noticed-by: brian m. carlson <sandals@crustytoothpaste.net> Helped-by: Derrick Stolee <stolee@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jeff King [Fri, 10 Apr 2026 09:06:08 +0000 (12:06 +0300)]
t1800: test SIGPIPE with parallel hooks
We recently fixed a bug in commit 2226ffaacd (run_processes_parallel():
fix order of sigpipe handling, 2026-04-08) where a hook that caused us
to get SIGPIPE would accidentally trigger the run_processes_parallel()
cleanup handler killing the child processes.
For a single hook, this meant killing the already-exited hook. This case
was triggered by our tests, but was only a problem on some platforms.
But if you have multiple hooks running in parallel, this causes a
problem everywhere, since one hook failing to read its input would take
down all hooks. Now that we have parallel hook support, we can add a
test for this case. It should pass already, due to the existing fix.
Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Adrian Ratiu <adrian.ratiu@collabora.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>