]> git.99rst.org Git - git.git/log
git.git
4 weeks agot: add greplint to detect bare grep assertions
Michael Montalbo [Mon, 6 Jul 2026 05:01:58 +0000 (05:01 +0000)]
t: add greplint to detect bare grep assertions

Without a lint guard, bare grep assertions will creep back into
tests over time, defeating the previous commit's conversion.

Add greplint.pl to catch bare 'grep' used as a test assertion
(where 'test_grep' should be used) and '! test_grep' (where
'test_grep !' should be used).

greplint.pl reuses the shared shell parser from lib-shell-parser.pl
to tokenize test bodies.  The Lexer collapses heredocs, command
substitutions, and quoted strings into single tokens, so 'grep'
appearing inside these contexts is not flagged.  A flat walk over
the token stream tracks command position and pipeline state to
distinguish assertion greps from filter greps.

For double-quoted test bodies, a source-line walk counts
backslash-continuation lines that the Lexer consumes without
emitting into the body text, adjusting the reported line number
accordingly.

Add test fixtures in greplint/ (modeled on chainlint/) covering
detection of bare grep assertions, correct skipping of filters,
pipelines, redirects, command substitutions, and lint-ok annotations.

Wire into the Makefile as:
  - test-greplint: runs greplint.pl on $(T) $(THELPERS) $(TPERF)
  - check-greplint: runs greplint.pl on fixtures, diffs against expected
  - clean-greplint: removes temp dir

Add eol=lf entries in t/.gitattributes for greplint fixtures,
matching chainlint, so that check-greplint passes on Windows
where core.autocrlf would otherwise cause CRLF mismatches
between expected and actual output.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
4 weeks agot: convert grep assertions to test_grep
Michael Montalbo [Mon, 6 Jul 2026 05:01:57 +0000 (05:01 +0000)]
t: convert grep assertions to test_grep

Replace bare grep with test_grep in test assertions across the
suite, including sourced test helpers (lib-*.sh, *-tests.sh).
test_grep prints the contents of the file being searched on
failure, making debugging easier than a bare grep which fails
silently.

Only assertion-style greps are converted: grep used as a filter
in pipelines, command substitutions, conditionals, or with
redirected I/O is left as-is with a "# lint-ok" annotation.
Existing '! test_grep' calls are rewritten to 'test_grep !' so
that the diagnostic output is preserved on failure.

test_grep requires the file it reads to exist, so '! grep'
assertions that inspect a file whose presence is conditional need
care.  In t5537 the '.git/shallow' file is still present after the
repack (the client remains shallow), so the assertion is
converted like any other.  In t1400 the '.git/packed-refs' file
exists only with the files backend, so its check is guarded with a
REFFILES prerequisite; the backend-agnostic 'git show-ref' check
that follows still runs under every backend.  In t7450 'git~2' is
the NTFS 8.3 short name of a '..git' file and only exists
when 8.3 short-name generation is enabled, so its check is guarded
with a 'test -f' on the path and uses test_grep inside the guard,
the same shape as t1400 (a plain test_grep would BUG when the
short name is absent).

The conversion was generated using a grep-assertion linter
(greplint.pl, added in the following commit) to identify bare
grep calls at command position.  To reproduce, from the t/
directory:

    # Step 1: annotate the two data-filter greps (grep produces
    # data, not a verdict) so the linter skips them.
    sed -i '/grep -vf before commits\.raw/s/$/ # lint-ok: data filter/' \
        t5326-multi-pack-bitmaps.sh
    sed -i '/grep -E "^\[0-9a-f\].*|| :/s/$/ # lint-ok: data filter/' \
        t5702-protocol-v2.sh

    # Step 1b: two '! grep' assertions need more than a mechanical
    # conversion; handle them by hand before the linter-driven steps
    # below so it leaves them alone.
    #
    # t1400: '.git/packed-refs' is absent under reftable, so guard the
    # check with REFFILES (a plain test_grep would BUG on the missing
    # file):
    #
    #      git update-ref -d HEAD $B &&
    #  -   ! grep "$m" .git/packed-refs &&
    #  +   if test_have_prereq REFFILES
    #  +   then
    #  +           test_grep ! "$m" .git/packed-refs
    #  +   fi &&
    #      test_must_fail git show-ref --verify -q $m
    #
    # t7450: git~2 is an NTFS 8.3 short name that exists only when
    # short-name generation is enabled, so guard the check on its
    # presence with 'test -f' and note in a comment why the path can
    # be absent (a plain test_grep would BUG when it is):
    #
    #  -   ! grep gitdir squatting-clone/d/a/git~2
    #  +   if test -f squatting-clone/d/a/git~2
    #  +   then
    #  +           test_grep ! gitdir squatting-clone/d/a/git~2
    #  +   fi

    # Step 2: reorder pre-existing '! test_grep' to 'test_grep !'
    # (must come before steps 3-4 so greplint does not see them)
    sed -i 's/! test_grep/test_grep !/' t0031-lockfile-pid.sh
    sed -i 's/! test_grep/test_grep !/' t5300-pack-object.sh
    sed -i 's/! test_grep/test_grep !/' t5319-multi-pack-index.sh

    # Step 3: convert '! grep' -> 'test_grep !'
    perl greplint.pl *.sh 2>&1 | cut -d: -f1,2 |
    while IFS=: read f l; do
        sed -i "${l}s/! *grep/test_grep !/" "$f"
    done

    # Step 4: convert remaining 'grep' -> 'test_grep'
    perl greplint.pl *.sh 2>&1 | cut -d: -f1,2 |
    while IFS=: read f l; do
        sed -i "${l}s/grep/test_grep/" "$f"
    done

To verify, run: make -C t test-greplint

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
4 weeks agot: fix Lexer line count for $() inside double-quoted strings
Michael Montalbo [Mon, 6 Jul 2026 05:01:56 +0000 (05:01 +0000)]
t: fix Lexer line count for $() inside double-quoted strings

scan_dqstring's post-loop newline counter re-counts newlines that
were already counted during recursive parsing of $() bodies.  This
happens because scan_dollar returns text containing newlines (from
multi-line command substitutions), and the catch-all counter at the
end of scan_dqstring counts all of them again.

Fix this by counting newlines inline as non-special characters are
consumed, and removing the post-loop catch-all.  Each newline is
now counted exactly once: literal newlines at the inline match,
line splices at the backslash handler, and $() newlines by
scan_token during the recursive parse.

This is a latent bug: any consumer that relies on token line
numbers rather than byte offsets would get incorrect results for
tokens following a multi-line $() inside a double-quoted string.
chainlint is not affected because it annotates the original body
text using byte offsets, not token line numbers.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
4 weeks agot: extract chainlint's parser into shared module
Michael Montalbo [Mon, 6 Jul 2026 05:01:55 +0000 (05:01 +0000)]
t: extract chainlint's parser into shared module

Move chainlint.pl's Lexer, ShellParser, and ScriptParser into a
shared module (lib-shell-parser.pl) so other lint tools can reuse
the same shell parsing infrastructure.  A subsequent commit adds
greplint.pl, which needs the same tokenizer to correctly identify
command boundaries.

ScriptParser's check_test() becomes a no-op in the shared module.
chainlint.pl defines ChainlintParser (extending ScriptParser)
with the &&-chain check_test() implementation.

No functional change: chainlint produces the same output and
check-chainlint self-tests pass.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
4 weeks agot: fix grep assertions missing file arguments
Michael Montalbo [Mon, 6 Jul 2026 05:01:54 +0000 (05:01 +0000)]
t: fix grep assertions missing file arguments

Three grep assertions were missing their file arguments, causing
them to read from empty stdin instead of the intended file:

- t2402: '! grep ...' should read from 'out', matching the
  grep on the preceding line.
- t7507: the closing quote is in the wrong place, making the
  entire 'diff --git actual' a single pattern with no file
  argument instead of pattern 'diff --git' and file 'actual'.
- t7700: '! grep ...' should read from 'packlist', matching
  the redirect on the preceding line.

Without file arguments these greps always succeed (empty stdin
matches nothing), so the assertions were not actually checking
anything.  All three tests pass with the corrected file arguments,
confirming the intended behavior is sound.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
4 weeks agot/README: document test_grep helper
Michael Montalbo [Mon, 6 Jul 2026 05:01:53 +0000 (05:01 +0000)]
t/README: document test_grep helper

test_grep is a wrapper around grep for test assertions that prints
the file contents on failure for easier debugging.  It also accepts
'!' as its first argument for negation, which preserves the
diagnostic output that '! test_grep' would suppress.

Despite being widely used (and the preferred replacement for bare
grep in assertions), test_grep has no entry in t/README alongside
the other documented helpers like test_cmp and test_line_count.
Add one.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
7 weeks agoMerge branch 'master' of https://github.com/j6t/git-gui
Junio C Hamano [Fri, 12 Jun 2026 12:41:40 +0000 (05:41 -0700)]
Merge branch 'master' of https://github.com/j6t/git-gui

* 'master' of https://github.com/j6t/git-gui:
  git-gui: silence install recipes under "make -s"
  git-gui: add gui and pick as explicit subcommands
  git-gui: check browser/blame arguments carefully
  git-gui: allow specifying path '.' to the browser
  git-gui: try harder to find worktree from gitdir
  git-gui: simplify [is_bare] to report if a worktree is known
  git-gui: use git rev-parse for worktree discovery
  git-gui: use rev-parse exclusively to find a repository
  git-gui: use --absolute-git-dir
  git-gui: do not change global vars in choose_repository::pick
  git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
  git-gui: remove unnecessary 'cd $_gitworktree' from do_gitk
  git-gui: use HEAD as current branch when detached

7 weeks agoMerge branch 'master' of https://github.com/j6t/gitk
Junio C Hamano [Fri, 12 Jun 2026 12:40:38 +0000 (05:40 -0700)]
Merge branch 'master' of https://github.com/j6t/gitk

* 'master' of https://github.com/j6t/gitk:
  gitk: add horizontal scrollbar to the commit list pane

7 weeks agoMerge branch 'horizontal-scroll' of github.com:ramcdona/gitk
Johannes Sixt [Fri, 12 Jun 2026 09:30:22 +0000 (11:30 +0200)]
Merge branch 'horizontal-scroll' of github.com:ramcdona/gitk

* 'horizontal-scroll' of github.com:ramcdona/gitk:
  gitk: add horizontal scrollbar to the commit list pane

Signed-off-by: Johannes Sixt <redacted>
7 weeks agoMerge branch 'ml/repo-discovery'
Johannes Sixt [Fri, 12 Jun 2026 09:05:28 +0000 (11:05 +0200)]
Merge branch 'ml/repo-discovery'

* ml/repo-discovery:
  git-gui: add gui and pick as explicit subcommands
  git-gui: check browser/blame arguments carefully
  git-gui: allow specifying path '.' to the browser
  git-gui: try harder to find worktree from gitdir
  git-gui: simplify [is_bare] to report if a worktree is known
  git-gui: use git rev-parse for worktree discovery
  git-gui: use rev-parse exclusively to find a repository
  git-gui: use --absolute-git-dir
  git-gui: do not change global vars in choose_repository::pick
  git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
  git-gui: remove unnecessary 'cd $_gitworktree' from do_gitk
  git-gui: use HEAD as current branch when detached

7 weeks agoGit 2.55-rc0 v2.55.0-rc0
Junio C Hamano [Thu, 11 Jun 2026 11:29:59 +0000 (04:29 -0700)]
Git 2.55-rc0

Signed-off-by: Junio C Hamano <redacted>
7 weeks agoMerge branch 'hn/macos-linker-warning'
Junio C Hamano [Thu, 11 Jun 2026 11:31:19 +0000 (04:31 -0700)]
Merge branch 'hn/macos-linker-warning'

A linker warning on macOS when building with Xcode 16.3 or newer has
been avoided by passing -fno-common to the compiler when a
sufficiently new linker is detected.

* hn/macos-linker-warning:
  config.mak.uname: avoid macOS linker warning on Xcode 16.3+

7 weeks agoMerge branch 'kk/wildmatch-windows-ls-files-prereq'
Junio C Hamano [Thu, 11 Jun 2026 11:31:19 +0000 (04:31 -0700)]
Merge branch 'kk/wildmatch-windows-ls-files-prereq'

In t3070-wildmatch, "via ls-files" test variants with patterns
containing backslash escapes are now skipped on Windows, avoiding 36
test failures caused by pathspec separator conversion.

* kk/wildmatch-windows-ls-files-prereq:
  t3070: skip ls-files tests with backslash patterns on Windows

7 weeks agoMerge branch 'mm/doc-word-diff'
Junio C Hamano [Thu, 11 Jun 2026 11:31:18 +0000 (04:31 -0700)]
Merge branch 'mm/doc-word-diff'

The documentation for "--word-diff" has been extended with a bit of
implementation detail of where these different words come from.

* mm/doc-word-diff:
  doc: clarify that --word-diff operates on line-level hunks

7 weeks agoMerge branch 'lp/http-fetch-pack-index-leak-fix'
Junio C Hamano [Thu, 11 Jun 2026 11:31:18 +0000 (04:31 -0700)]
Merge branch 'lp/http-fetch-pack-index-leak-fix'

A memory leak in `fetch_and_setup_pack_index()` when verification of
the downloaded pack index fails has been plugged. Also an obsolete
`unlink()` call on parse failure has been cleaned up.

* lp/http-fetch-pack-index-leak-fix:
  http: fix memory leak in fetch_and_setup_pack_index()
  http: cleanup function fetch_and_setup_pack_index()

7 weeks agoMerge branch 'ps/odb-source-loose'
Junio C Hamano [Thu, 11 Jun 2026 11:31:18 +0000 (04:31 -0700)]
Merge branch 'ps/odb-source-loose'

The loose object source has been refactored into a proper `struct
odb_source`.

* ps/odb-source-loose:
  odb/source-loose: drop pointer to the "files" source
  odb/source-loose: stub out remaining callbacks
  odb/source-loose: wire up `write_object_stream()` callback
  object-file: refactor writing objects to use loose source
  odb/source-loose: wire up `write_object()` callback
  loose: refactor object map to operate on `struct odb_source_loose`
  odb/source-loose: wire up `freshen_object()` callback
  odb/source-loose: drop `odb_source_loose_has_object()`
  odb/source-loose: wire up `count_objects()` callback
  odb/source-loose: wire up `find_abbrev_len()` callback
  odb/source-loose: wire up `for_each_object()` callback
  odb/source-loose: wire up `read_object_stream()` callback
  odb/source-loose: wire up `read_object_info()` callback
  odb/source-loose: wire up `close()` callback
  odb/source-loose: wire up `reprepare()` callback
  odb/source-loose: start converting to a proper `struct odb_source`
  odb/source-loose: store pointer to "files" instead of generic source
  odb/source-loose: move loose source into "odb/" subsystem

7 weeks agoMerge branch 'mm/line-log-cleanup'
Junio C Hamano [Thu, 11 Jun 2026 11:31:17 +0000 (04:31 -0700)]
Merge branch 'mm/line-log-cleanup'

The `git log -L` implementation has been refactored to use the
standard diff output pipeline, enabling pickaxe and diff-filter to
work as expected. Additionally, metadata-only diff formats like
--raw and --name-only are now supported with -L.

* mm/line-log-cleanup:
  line-log: allow non-patch diff formats with -L
  line-log: integrate -L output with the standard log-tree pipeline
  revision: move -L setup before output_format-to-diff derivation

7 weeks agoMerge branch 'st/daemon-sockaddr-fixes'
Junio C Hamano [Thu, 11 Jun 2026 11:31:17 +0000 (04:31 -0700)]
Merge branch 'st/daemon-sockaddr-fixes'

Correct use of sockaddr API in "git daemon".

* st/daemon-sockaddr-fixes:
  daemon: guard NULL REMOTE_PORT in execute() logging
  daemon: fix IPv6 address truncation in ip2str()
  daemon: fix IPv6 address corruption in lookup_hostname()

7 weeks agoThe 13th batch
Junio C Hamano [Tue, 9 Jun 2026 01:04:31 +0000 (10:04 +0900)]
The 13th batch

Signed-off-by: Junio C Hamano <redacted>
7 weeks agoMerge branch 'jc/doc-monitor-ghci'
Junio C Hamano [Tue, 9 Jun 2026 01:04:51 +0000 (10:04 +0900)]
Merge branch 'jc/doc-monitor-ghci'

Encourage original authors to monitor the CI status.

* jc/doc-monitor-ghci:
  SubmittingPatches: proactively monitor GHCI pages

7 weeks agoMerge branch 'ib/doc-push-default-simple'
Junio C Hamano [Tue, 9 Jun 2026 01:04:51 +0000 (10:04 +0900)]
Merge branch 'ib/doc-push-default-simple'

The documentation for `push.default = simple` has been clarified to
better explain its behavior, making it clear that it pushes the
current branch to a same-named branch on the remote, and detailing
the upstream requirements for centralized workflows.

* ib/doc-push-default-simple:
  doc: clarify push.default=simple behavior

7 weeks agoMerge branch 'gh/jump-auto-mode'
Junio C Hamano [Tue, 9 Jun 2026 01:04:50 +0000 (10:04 +0900)]
Merge branch 'gh/jump-auto-mode'

The 'git-jump' command (in contrib/) has been taught to automatically
pick a mode (merge, diff, or ws) when invoked without arguments.

* gh/jump-auto-mode:
  git-jump: pick a mode automatically when invoked without arguments

7 weeks agoMerge branch 'rs/strbuf-add-oid-hex'
Junio C Hamano [Tue, 9 Jun 2026 01:04:50 +0000 (10:04 +0900)]
Merge branch 'rs/strbuf-add-oid-hex'

Formatting object name in full hexadecimal form has been optimized
by using a new strbuf_add_oid_hex() helper function.

* rs/strbuf-add-oid-hex:
  hex: add and use strbuf_add_oid_hex()

7 weeks agoMerge branch 'rs/strbuf-add-uint'
Junio C Hamano [Tue, 9 Jun 2026 01:04:50 +0000 (10:04 +0900)]
Merge branch 'rs/strbuf-add-uint'

Adding a decimal integer with strbuf_addf("%u") appears commonly;
they have been optimized by using a custom formatter.

* rs/strbuf-add-uint:
  ls-tree: use strbuf_add_uint()
  ls-files: use strbuf_add_uint()
  cat-file: use strbuf_add_uint()
  strbuf: add strbuf_add_uint()

7 weeks agoMerge branch 'ua/push-remote-group'
Junio C Hamano [Tue, 9 Jun 2026 01:04:50 +0000 (10:04 +0900)]
Merge branch 'ua/push-remote-group'

"git push" learned to take a "remote group" name to push to, which
causes pushes to multiple places, just like "git fetch" would do.

* ua/push-remote-group:
  push: support pushing to a remote group
  remote: move remote group resolution to remote.c
  remote: fix sign-compare warnings in push_cas_option

7 weeks agoMerge branch 'th/promisor-quiet-per-repo'
Junio C Hamano [Tue, 9 Jun 2026 01:04:49 +0000 (10:04 +0900)]
Merge branch 'th/promisor-quiet-per-repo'

The "promisor.quiet" configuration variable was not used from
relevant submodules when commands like "grep --recurse-submodules"
triggered a lazy fetch, which has been corrected.

* th/promisor-quiet-per-repo:
  promisor-remote: fix promisor.quiet to use the correct repository

7 weeks agoMerge branch 'tb/bitmap-build-performance'
Junio C Hamano [Tue, 9 Jun 2026 01:04:49 +0000 (10:04 +0900)]
Merge branch 'tb/bitmap-build-performance'

Reachability bitmap generation has been significantly optimized. By
reordering tree traversal, caching object positions, and refining how
pseudo-merge bitmaps are constructed, the performance of "git repack
--write-midx-bitmaps" is improved, especially for large repositories
and when using pseudo-merges.

* tb/bitmap-build-performance:
  pack-bitmap: build pseudo-merge bitmaps after regular bitmaps
  pack-bitmap: remember pseudo-merge parents
  pack-bitmap: sort bitmaps before XORing
  pack-bitmap: cache object positions during fill
  pack-bitmap: consolidate `find_object_pos()` success path
  pack-bitmap: reuse stored selected bitmaps
  pack-bitmap: check subtree bits before recursing
  pack-bitmap: pass object position to `fill_bitmap_tree()`

8 weeks agoThe 12th batch
Junio C Hamano [Sun, 7 Jun 2026 14:58:12 +0000 (23:58 +0900)]
The 12th batch

Signed-off-by: Junio C Hamano <redacted>
8 weeks agoMerge branch 'ja/doc-synopsis-style-again'
Junio C Hamano [Sun, 7 Jun 2026 14:58:25 +0000 (23:58 +0900)]
Merge branch 'ja/doc-synopsis-style-again'

A batch of documentation pages has been updated to use the modern
synopsis style.

* ja/doc-synopsis-style-again:
  doc: convert git-imap-send synopsis and options to new style
  doc: convert git-apply synopsis and options to new style
  doc: convert git-am synopsis and options to new style
  doc: convert git-grep synopsis and options to new style
  doc: git bisect: clarify the usage of the synopsis vs actual command
  doc: convert git-bisect to synopsis style

8 weeks agoMerge branch 'kk/commit-reach-optim'
Junio C Hamano [Sun, 7 Jun 2026 14:58:25 +0000 (23:58 +0900)]
Merge branch 'kk/commit-reach-optim'

The check for non-stale commits in the priority queue used by
`paint_down_to_common` and `ahead_behind` has been optimized by
replacing an O(N) scan with an O(1) counter, yielding performance
improvements in repositories with wide histories.

* kk/commit-reach-optim:
  commit-reach: replace queue_has_nonstale() scan with O(1) tracking
  commit-reach: deduplicate queue entries in paint_down_to_common
  object.h: fix stale entries in object flag allocation table

8 weeks agoMerge branch 'aj/stash-patch-optimize-temporary-index'
Junio C Hamano [Sun, 7 Jun 2026 14:58:24 +0000 (23:58 +0900)]
Merge branch 'aj/stash-patch-optimize-temporary-index'

"git stash -p" has been optimized by reusing cached index
entries in its temporary index, avoiding unnecessary lstat()
calls on unchanged files.

* aj/stash-patch-optimize-temporary-index:
  stash: reuse cached index entries in --patch temporary index

8 weeks agoMerge branch 'kh/free-commit-list'
Junio C Hamano [Sun, 7 Jun 2026 14:58:24 +0000 (23:58 +0900)]
Merge branch 'kh/free-commit-list'

Code clean-up.

* kh/free-commit-list:
  commit: remove deprecated functions
  *: replace deprecated free_commit_list

8 weeks agoMerge branch 'ds/restore-sparse-index'
Junio C Hamano [Sun, 7 Jun 2026 14:58:24 +0000 (23:58 +0900)]
Merge branch 'ds/restore-sparse-index'

'git restore --staged' has been optimized to avoid unnecessarily expanding
the sparse index when operating on paths within the sparse checkout
definition, by handling sparse directory entries at the tree level.

* ds/restore-sparse-index:
  restore: avoid sparse index expansion
  t1092: test 'git restore' with sparse index

8 weeks agoMerge branch 'ar/receive-pack-worktree-env'
Junio C Hamano [Sun, 7 Jun 2026 14:58:24 +0000 (23:58 +0900)]
Merge branch 'ar/receive-pack-worktree-env'

The GIT_WORK_TREE variable prepared to invoke the push-to-checkout
hook was leaking into the environment even when there was no hook
used and broke the default push-to-deploy (i.e., let "git checkout"
update the working tree only when the working tree is clean).

* ar/receive-pack-worktree-env:
  receive-pack: fix updateInstead with core.worktree

8 weeks agogit-gui: silence install recipes under "make -s"
Harald Nordgren [Thu, 4 Jun 2026 06:48:50 +0000 (06:48 +0000)]
git-gui: silence install recipes under "make -s"

Several install and uninstall recipes embed "echo" calls that fire as
part of the recipe itself, so the install banners (DEST, INSTALL,
LINK, REMOVE) were visible whenever the variables expand non-empty.

Guard the whole "ifndef V" block on "-s" so the loud variants are
selected only when "-s" is absent and V=1 is unset. The existing
"-s" check also had its findstring arguments in the wrong order
(needle "-s" never fit in haystack "s"), so swap them while moving
the check to wrap the block.

Signed-off-by: Harald Nordgren <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: add gui and pick as explicit subcommands
Mark Levedahl [Sun, 31 May 2026 23:02:25 +0000 (19:02 -0400)]
git-gui: add gui and pick as explicit subcommands

git-gui accepts subcommands blame | browser | citool, and assumes the
subcommand is 'gui' if none is actually given, But, git-gui also has a
repository picker (choose_repository::pick) that can create a new
repository + worktree, or choose an existing one, switch to that, and
the run the gui. The user has no direct control over invoking the
picker, instead the picker is triggered by failure in the repository /
worktree discovery process: this includes being started in a directory
not controlled by git, which is probably the intended use case.

The picker can appear when the user has no intention of creating a new
worktree, and the user cannot use the picker to create a new worktree
inside another.

So, add two explicit subcommands:
    gui  - Run the gui if repository/worktree discovery succeeds, or die
           with an error message, but never run the picker.
    pick - First run the picker, regardless, then start the gui in
           the chosen worktree.

Nothing in this changes the prior behavior, the alternates above must be
explicitly selected to see any change.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: check browser/blame arguments carefully
Mark Levedahl [Sun, 31 May 2026 23:02:24 +0000 (19:02 -0400)]
git-gui: check browser/blame arguments carefully

git gui offers two related commands, browser and blame, that provide
graphical interfaces driven by git ls-tree and git blame. As such, the
arguments to git-gui need to satisfy those two git commands. But,
git-gui does not assure this leading to confusing or incorrect results.
For instance 'git browser <non-existent path>' shows a blank browser
window rather than error message.

Also, commit 3e45ee1ef2 ("git-gui: Smarter command line parsing for
browser, blame", 2007-05-08) implemented code to allow giving path
before rev on the command line, and unconditionally uses the worktree to
disambiguate. As a result, the following command run in a current
git-gui checkout of the master branch shows the master branch version of
blame.tcl, when none should be shown as that file does not exist in
gitgui-0.6.0.

  git gui blame lib/blame.tcl gitgui-0.6.0

This 'file before rev' feature in git-gui mirrors ideas considered when
git's user interface was very young, but no such feature is documented
for any git command.  Rather than try to fix an idea git itself
rejected, let's just remove this broken and hopefully unused feature.

git-gui browser|blame both accept 'rev' and 'path' as command line
arguments.  rev defaults to 'HEAD' if not given, while path must be
given. path names a directory tree to ls-tree or a file to blame. path
must exist in rev for ls-tree and for blame.  In addition git blame will
include uncommitted changes from the worktree file at 'path' if rev is
not given (thus defaulting to HEAD), but still requires that the file
exists in HEAD.

So, let's clean up the parser to check that the arguments are usable.
- give a full synopsis, including '--' that may be used to separate rev and
  path. (as path is the required final arg, -- gives no extra info)
- explicitly check the number of arguments
- use rev-parse to assure a user supplied rev is valid
- use ls-tree to assure that path exists in rev
- for blame only, with no rev given and a worktree existing, also assure
  that path points to a file in the worktree

With these changes, error messages are thrown by the parser if the path
or rev are not known: no blank or erroneous displays are created. Also,
this avoids accessing the worktree except in the specific use case
supported by blame / git-blame, meaning browser|blame now also work
without a worktree.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: allow specifying path '.' to the browser
Mark Levedahl [Sun, 31 May 2026 23:02:23 +0000 (19:02 -0400)]
git-gui: allow specifying path '.' to the browser

Invoking "git-gui browser rev ." should show the file browser for the
commitish rev, starting at the current directory. When the current
directory is the working tree root, this errors out in normalize_relpath
because the '.' is removed, yielding an empty list as argument to [file
join ...]. git ls-tree (underlying the browser) accepts '.', so use that
as the value when in the root.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: try harder to find worktree from gitdir
Mark Levedahl [Sun, 31 May 2026 23:02:22 +0000 (19:02 -0400)]
git-gui: try harder to find worktree from gitdir

git-gui, since 87cd09f43e ("git-gui: work from the .git dir",
2010-01-23), has had the intent to allow starting from inside a
repository, then switching to the parent directory if that is a valid
worktree.

This certainly hasn't worked since 2d92ab32fd ("rev-parse: make
--show-toplevel without a worktree an error", 2019-11-19) in git, but
breaking this git-gui feature was unintentional.

There are (at least) 3 cases where the gitdir can tell us where the
worktree is, and we would like all to work:

- core.worktree is set, and points to a valid worktree. This is already
  handled  by git rev-parse --show-toplevel, even when not in the worktree.
  There is nothing more to do in this case.

- the gitdir is embedded in a worktree as subdirectory .git. The parent
  is (or at least should be) a valid worktree. This worked long ago.

- the gitdir is a worktree specific directory (under
  <mainrepo>/worktrees/worktree_name), within which there is a file
  "gitdir" pointing to .git in the worktree. git gui never learned to
  handle this case.

Let's handle the latter two cases. Always check that the discovered
worktree is valid and points to the already discovered gitdir according
to git rev-parse. This avoids issues that may arise because we are
discovering from the gitdir up, rather than the worktree down, and file
system non-posix behavior or misconfiguration of git might cause
confusion.  For instance, a manually moved worktree might not be where
the gitdir points, or the gitdir might be configured with
core.bare=true.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: simplify [is_bare] to report if a worktree is known
Mark Levedahl [Sun, 31 May 2026 23:02:21 +0000 (19:02 -0400)]
git-gui: simplify [is_bare] to report if a worktree is known

git-gui includes proc is_bare, used in several places to make decisions
on whether a worktree exists, but also in discovery to tell if a
worktree can be supported.

But, is_bare is out of date with regard to multiple worktrees, safe
repository guards, and possibly other relevant features known to git
rev-parse. Also, is_bare caches its result on the first call, so is not
useful if a later step in the discovery process finds a worktree.

So, simplify is_bare to report whether git-gui has a worktree or is
working only from a repository.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: use git rev-parse for worktree discovery
Mark Levedahl [Sun, 31 May 2026 23:02:20 +0000 (19:02 -0400)]
git-gui: use git rev-parse for worktree discovery

git gui uses a combination of tcl code and git invocations to determine
the worktree and the location with respect to the worktree root
(_prefix). But, git rev-parse provides all of this information directly,
and assures full error and configuration checking are done by git
itself. The entirety of discovery in normal configurations involves

git rev-parse --show-toplevel (gets worktree root)
git rev-parse --show-prefix (shows location wrt the root)

An error thrown on either of these lines means the worktree discovered
by git is unusable, or git did not discover a worktree because the
current directory is inside the repository. If the user has defined
GIT_DIR or GIT_WORK_TREE, this is a user configuration error and git-gui
should stop.

Otherwise, the blame or browser subcommands can be used without a
worktree.

A separate error might occur when changing to the root of the discovered
worktree. The cause would be file system related and completely outside
of git's control, so trap that independently.

Discovery of the repository and the worktree must be guarded to trap
errors: the intent is that any configuration problems are caught during
discovery, and later processing need not include error trapping and
recovery. So, move all worktree discovery code to be immediately after
repository discovery.

This does move configuration loading to occur after worktree discovery
rather than before. None of the code executed in worktree discovery has
any option controlled by a git-gui configuration variable, so no impact
is expected. git itself will always read the repository configuration,
including worktree specific configuration data if that exists, so this
is unaffected by when git-gui loads its own config data. Also, we cannot
be sure the worktree dependent configuration can be loaded before
full discovery is complete.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: use rev-parse exclusively to find a repository
Mark Levedahl [Sun, 31 May 2026 23:02:19 +0000 (19:02 -0400)]
git-gui: use rev-parse exclusively to find a repository

git-gui attempts to use env(GIT_DIR) directly as the git repository,
accepting GIT_DIR if it is a directory. Only if that fails is git
rev-parse used to discover the repository.  But, this avoids all of
git-core's validity checking on a repository, thus possibly deferring an
error to a later step, possibly unexpected. Repository validation should
be part of initial setup so that later processing does not need error
trapping for configuration errors.

Let's just invoke rev-parse so all error checking is done.

While here, let's cleanup the error handling.

Stop if an error occurs and the user set GIT_DIR or GIT_WORK_TREE.
Use of either or both of those variables is supported by git, but their
use also means the user has taken responsibility that they are correct,
so a failure is something the user must address.

Otherwise on error, continue the existing behavior and show the
repository picker. But, let's move the possible invocation of
repository_chooser::pick to a separate code block. This permits adding
separate conditions on using pick independent of repository discovery, and
will be exploited later in the series.  Note that the picker always
returns with the current directory in the root of a worktree with the
git repository is in the .git subdirectory.  The variable "picked" is
used by git-gui to automatically execute the "Explore Working Copy" menu
item after the repository picker is run.  This is controlled by config
variable gui.autoexplore, and happens after all discovery is complete.

Remove a later check on whether _gitdir is a directory: that code
cannot be reached without rev-parse already validating the repository.

_prefix is set as part of worktree discovery, but must be {} if not
running with a worktree. Initialze this as {} along with other global
variables, this is the correct value is no worktree is found.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: use --absolute-git-dir
Mark Levedahl [Sun, 31 May 2026 23:02:18 +0000 (19:02 -0400)]
git-gui: use --absolute-git-dir

git-gui uses git rev-parse --git-dir to get the pathname of the
discovered git repository. The returned value can be relative, and is
'.' if the current directory is the top of the repository directory
itself.  git-gui has code to change '.' to [pwd] in this case so that
subsequent logic runs.

But, git rev-parse supports --absolute-git-dir from fac60b8925
("rev-parse: add option for absolute or relative path formatting",
2020-12-13), and included in git 2.31. git-gui requires git >= 2.36, so
this more useful form is always available. Use --absolute-git-dir to
always get an absolute path, avoiding the need for other checks, and
delete the now unneeded code to fix a relative _gitdir.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: do not change global vars in choose_repository::pick
Mark Levedahl [Sun, 31 May 2026 23:02:17 +0000 (19:02 -0400)]
git-gui: do not change global vars in choose_repository::pick

The repository picker (choose_repository::pick, AKA pick) on success
always returns with the current directory at the root of the selected
worktree, with the global variable _gitdir holding the name of the
git repository, possibly as a relative path, and _prefix {}. The
worktree root (_gitworktree) is not filled out, and if the selection was
from the "recent" list, no validation has occurred beyond testing that
the worktree root exists. So, repository and worktree validation are
still needed to be sure the new repo + worktree is usable.

pick only supports worktrees with a .git entry in the worktree root, so
git repository and worktree discovery will work starting in the current
directory on return. In cases of error, or user abort, pick exits the
process rather than returning.

So, let's change pick to not alter any global values, with success
indicated by the process returning to the caller. In this case, the
current directory is the worktree root, with a .git entry. The caller
then proceeds with normal discovery to find and validate both repository
and worktree.

With this, pick now returns 1 in the success case, but additional work
would be necessary to return from conditions where 0 should be returned.
Checking this return value would be superfluous.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE
Mark Levedahl [Sun, 31 May 2026 23:02:16 +0000 (19:02 -0400)]
git-gui: guard set/unset of GIT_DIR and GIT_WORK_TREE

git-gui unconditionally exports _gitdir as GIT_DIR, and _gitworktree as
GIT_WORK_TREE, to the environment, and unconditionally
unsets these environment variables before invoking gitk or git-gui when
a submodule is involved. This export happens even if _gitworktree is
empty, which happens when running from a bare repository. However,
exporting GIT_WORK_TREE as empty is never valid, and causes errors in
git.

GIT_DIR must be exported if the repository is not discoverable from the
worktree (or current directory if there is no worktree). The user might
have configured this.

If there is a worktree, git-gui makes this the current directory.
However, if the repository sets core.worktree, this value can only be
overridden by GIT_WORK_TREE so the latter must be exported.

As we cannot eliminate conditions where either variable is needed, let's
implement a pair of functions to set / unset these variables without
error, and without ever exporting an empty GIT_WORK_TREE.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agoconfig.mak.uname: avoid macOS linker warning on Xcode 16.3+
Harald Nordgren [Tue, 2 Jun 2026 07:37:58 +0000 (07:37 +0000)]
config.mak.uname: avoid macOS linker warning on Xcode 16.3+

Building on macOS with Xcode 16.3 or newer emits:

    ld: warning: reducing alignment of section __DATA,__common
    from 0x8000 to 0x4000 because it exceeds segment maximum
    alignment

Pass -fno-common when "ld -v" reports ld-1167 or newer, so tentative
definitions of large arrays go into BSS instead of __DATA,__common.

Signed-off-by: Harald Nordgren <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoThe 11th batch
Junio C Hamano [Tue, 2 Jun 2026 03:48:59 +0000 (12:48 +0900)]
The 11th batch

Signed-off-by: Junio C Hamano <redacted>
2 months agoMerge branch 'kh/doc-hook'
Junio C Hamano [Tue, 2 Jun 2026 07:15:29 +0000 (16:15 +0900)]
Merge branch 'kh/doc-hook'

Doc updates.

* kh/doc-hook:
  doc: hook: don’t self-link via config include
  doc: config: include existing git-hook(1) section
  doc: hook: consistently capitalize Git
  doc: hook: remove stray backtick

2 months agoMerge branch 'ds/path-walk-filters'
Junio C Hamano [Tue, 2 Jun 2026 07:15:29 +0000 (16:15 +0900)]
Merge branch 'ds/path-walk-filters'

The "git pack-objects --path-walk" traversal has been integrated
with several object filters, including blobless and sparse filters.

* ds/path-walk-filters:
  path-walk: support `combine` filter
  path-walk: support `object:type` filter
  path-walk: support `tree:0` filter
  t6601: tag otherwise-unreachable trees
  pack-objects: support sparse:oid filter with path-walk
  path-walk: add pl_sparse_trees to control tree pruning
  path-walk: support blob size limit filter
  backfill: die on incompatible filter options
  path-walk: support blobless filter
  path-walk: always emit directly-requested objects
  t/perf: add pack-objects filter and path-walk benchmark
  pack-objects: pass --objects with --path-walk
  t5620: make test work with path-walk var

2 months agoMerge branch 'ta/approxidate-noon-fix'
Junio C Hamano [Tue, 2 Jun 2026 07:15:29 +0000 (16:15 +0900)]
Merge branch 'ta/approxidate-noon-fix'

"Friday noon" asked in the morning on Sunday was parsed to be one
day before the specified time, which has been corrected.

* ta/approxidate-noon-fix:
  approxidate: use deferred mday adjustments for "specials"
  approxidate: make "specials" respect fixed day-of-month
  t0006: add support for approxidate test date adjustment
  approxidate: make "today" wrap to midnight

2 months agoMerge branch 'jk/connect-service-enum'
Junio C Hamano [Tue, 2 Jun 2026 07:15:28 +0000 (16:15 +0900)]
Merge branch 'jk/connect-service-enum'

The "name" argument in git_connect() and related functions has been
converted to a "service" enum to improve type safety and clarify its
purpose.

* jk/connect-service-enum:
  transport-helper: fix typo in BUG() message
  connect: use "service" enum for "name" argument

2 months agohttp: fix memory leak in fetch_and_setup_pack_index()
LorenzoPegorari [Mon, 1 Jun 2026 13:52:12 +0000 (15:52 +0200)]
http: fix memory leak in fetch_and_setup_pack_index()

Inside the function `fetch_and_setup_pack_index()`, when the pack
obtained using `parse_pack_index()` fails to be verified by
`verify_pack_index()`, the function returns without closing and freeing
said pack.

Fix this by calling `close_pack_index()` to munmap the index file for
the leaking pack (which might have been mmapped by `fetch_pack_index()`
or `verify_pack_index()`), and then free it, when the verification
fails.

Signed-off-by: LorenzoPegorari <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agohttp: cleanup function fetch_and_setup_pack_index()
LorenzoPegorari [Mon, 1 Jun 2026 13:52:01 +0000 (15:52 +0200)]
http: cleanup function fetch_and_setup_pack_index()

Cleanup the function `fetch_and_setup_pack_index()` by removing the
useless call to the function `unlink()`.

This is not necessary anymore since 63aca3f7f1 (dumb-http: store
downloaded pack idx as tempfile, 2024-10-25), when `fetch_pack_index()`
started registering its return value (in this case `tmp_idx`) as a
tempfile to be deleted at process exit.

Signed-off-by: LorenzoPegorari <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: drop pointer to the "files" source
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:41 +0000 (10:20 +0200)]
odb/source-loose: drop pointer to the "files" source

Now that all callbacks of the loose source operate on `struct
odb_source_loose` directly we no longer have to reach into the "files"
source at all.

Drop this field and update `odb_source_loose_new()` to instead accept
all parameters required to initialize itself. This ensures that the
"loose" backend is a fully standalone source.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: stub out remaining callbacks
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:40 +0000 (10:20 +0200)]
odb/source-loose: stub out remaining callbacks

Stub out remaining callback functions for the "loose" backend.

Note that we also stub out transactions for loose objects. In fact, we
already have the infrastructure in place for those, and we could in
theory implement those, as well. But there are separate efforts ongoing
to polish up transactional interfaces, and doing so now would likely
result in some messiness. This omission will thus be worked on in a
subsequent patch series, once the dust has settled.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `write_object_stream()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:39 +0000 (10:20 +0200)]
odb/source-loose: wire up `write_object_stream()` callback

Wire up the `write_object_stream()` callback.

Note that we don't move the implementation into "odb/source-loose.c".
This is because most of the logic to write loose objects is still
contained in "object-file.c", and detangling that requires us to do some
refactorings as explained in the preceding commit. So for now, the
implementation of writing an object stream is still located in
"object-file.c".

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoobject-file: refactor writing objects to use loose source
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:38 +0000 (10:20 +0200)]
object-file: refactor writing objects to use loose source

The "object-file" subsystem still hosts the majority of logic used to
write loose objects. Eventually, we'll want to move this logic into
"odb/source-loose.c", but this isn't yet easily possible because a lot
of the writing logic is still being shared with `force_object_loose()`.

We will eventually detangle this logic so that we can indeed move all of
it into the "loose" source. Meanwhile though, refactor the code so that
it operates on a `struct odb_source_loose` directly to already make the
dependency explicit.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `write_object()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:37 +0000 (10:20 +0200)]
odb/source-loose: wire up `write_object()` callback

Move `odb_source_loose_write_object()` from "object-file.c" into
"odb/source-loose.c" and wire it up as the `write_object()` callback of
the loose source.

As in preceding commits, this requires us to expose a couple of generic
functions from "object-file.c" as they are used in both subsystems now.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoloose: refactor object map to operate on `struct odb_source_loose`
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:36 +0000 (10:20 +0200)]
loose: refactor object map to operate on `struct odb_source_loose`

While the loose object map functions in "loose.c" accept a generic
`struct odb_source *`, they always expect this to be the "files"
backend. Furthermore, the subsystem doesn't even care about the "files"
backend, but only uses it as a stepping stone to get to the "loose"
backend.

This assumption is implicit and thus not immediately obvious. Refactor
the interfaces to instead operate on a `struct odb_source_loose`
instead, which eliminates the implicit dependency and unnecessary detour
via the "files" source.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `freshen_object()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:35 +0000 (10:20 +0200)]
odb/source-loose: wire up `freshen_object()` callback

Move `odb_source_loose_freshen_object()` from "object-file.c" into
"odb/source-loose.c" and wire it up as the `freshen_object()` callback
of the loose source.

As part of the move, `check_and_freshen_source()` is inlined into the
callback function, as it has no other callers anymore.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: drop `odb_source_loose_has_object()`
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:34 +0000 (10:20 +0200)]
odb/source-loose: drop `odb_source_loose_has_object()`

The function `odb_source_loose_has_object()` checks whether a specific
object exists as a loose object on disk by using lstat(3p). This
interface is somewhat redundant, as we typically check for object
existence in a generic way via `odb_source_read_object_info()`.

In fact, these two calls are redundant in case the latter is called in a
specific way: when called without an object info request and without the
`OBJECT_INFO_QUICK` flag, then we will end up doing the same call to
lstat(3p) in `read_object_info_from_path()`.

Drop the function and adapt callers to instead use the generic
interface so that its calling conventions align with that of other
sources.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `count_objects()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:33 +0000 (10:20 +0200)]
odb/source-loose: wire up `count_objects()` callback

Move `odb_source_loose_count_objects()` and its associated helpers from
"object-file.c" into "odb/source-loose.c" and wire it up as the
`count_objects()` callback of the loose source.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `find_abbrev_len()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:32 +0000 (10:20 +0200)]
odb/source-loose: wire up `find_abbrev_len()` callback

Move `odb_source_loose_find_abbrev_len()` and its associated helpers
from "object-file.c" into "odb/source-loose.c" and wire it up as the
`find_abbrev_len` callback of the loose source.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `for_each_object()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:31 +0000 (10:20 +0200)]
odb/source-loose: wire up `for_each_object()` callback

Move `odb_source_loose_for_each_object()` and its associated helpers
from "object-file.c" into "odb/source-loose.c" and wire it up as the
`for_each_object()` callback of the loose source.

Again, as in the preceding commit, we are forced to expose a couple of
functions from "object-file.c" that are now used by both subsystems.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `read_object_stream()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:30 +0000 (10:20 +0200)]
odb/source-loose: wire up `read_object_stream()` callback

Move `odb_source_loose_read_object_stream()` and its associated helpers
from "object-file.c" into "odb/source-loose.c" and wire it up as the
`read_object_stream()` callback of the loose source.

As part of the move we are also forced to expose a couple of functions
from "object-file.h" that parse object headers in a somewhat-generic
way, as those functions are now used by both subsystems.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `read_object_info()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:29 +0000 (10:20 +0200)]
odb/source-loose: wire up `read_object_info()` callback

Move `odb_source_loose_read_object_info()` from "object-file.c" into
"odb/source-loose.c" and wire it up as the `read_object_info()` callback
of the loose source. Callers that previously invoked it directly now go
through the generic `odb_source_read_object_info()` interface instead.

The function `read_object_info_from_path()` cannot be moved along with
it because it is still called by `for_each_object_wrapper_cb()`. It is
therefore kept in place, but adjusted to take a loose source to clarify
that it's always operating on this structure.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `close()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:28 +0000 (10:20 +0200)]
odb/source-loose: wire up `close()` callback

Wire up a new `close()` callback for the loose source and call it from
the "files" source via the generic `odb_source_close()` interface. The
callback itself is a no-op as the loose source has no resources that
need to be released on close.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: wire up `reprepare()` callback
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:27 +0000 (10:20 +0200)]
odb/source-loose: wire up `reprepare()` callback

Move `odb_source_loose_reprepare()` from "object-file.c" into
"odb/source-loose.c" and wire it up as the `reprepare()` callback of the
loose source.

While at it, make `odb_source_loose_clear_cache()` static, as it is no
longer needed outside of its file.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: start converting to a proper `struct odb_source`
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:26 +0000 (10:20 +0200)]
odb/source-loose: start converting to a proper `struct odb_source`

Start converting `struct odb_source_loose` into a proper pluggable
`struct odb_source` by embedding the base struct and assigning it the
new `ODB_SOURCE_LOOSE` type. Furthermore, wire up lifecycle management
of this source by implementing the `free` callback and taking ownership
of the chdir notifications.

Note that the loose source is not yet functional as a standalone `struct
odb_source`, as it's missing all of the callback implementations. These
will be wired up in subsequent commits.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: store pointer to "files" instead of generic source
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:25 +0000 (10:20 +0200)]
odb/source-loose: store pointer to "files" instead of generic source

The `struct odb_source_loose` holds a pointer to its owning parent
source. The way that Git is currently structured, this parent is always
the "files" source. In subsequent commits we're going to detangle that
so that the "loose" source doesn't have any owning parent source at all
so that it can be used as a completely standalone source.

Detangling this mess is somewhat intricate though, and is made even more
intricate because it's not always clear which kind of source one is
holding at a specific point in time -- either the parent "files" source,
or the child "loose" source.

Make this relationship more explicit by storing a pointer to the "files"
source instead of storing a pointer to a generic `struct odb_source`.
This will help make subsequent steps a bit clearer.

Note that this is a temporary step, only. At the end of this series
we will have dropped the parent pointer completely.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoodb/source-loose: move loose source into "odb/" subsystem
Patrick Steinhardt [Mon, 1 Jun 2026 08:20:24 +0000 (10:20 +0200)]
odb/source-loose: move loose source into "odb/" subsystem

In subsequent patches we'll be turning `struct odb_source_loose` into a
proper `struct odb_source`. As a first step towards this goal, move its
struct out of "object-file.c" and into "odb/source-loose.c".

This detaches the implementation of the loose object source from the
generic object file code, following the same convention already used by
the "files" and "in-memory" sources.

No functional changes are intended.

Signed-off-by: Patrick Steinhardt <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agogit-gui: remove unnecessary 'cd $_gitworktree' from do_gitk
Johannes Sixt [Sun, 31 May 2026 23:02:15 +0000 (19:02 -0400)]
git-gui: remove unnecessary 'cd $_gitworktree' from do_gitk

In the procedure that invokes Gitk, we have a 'cd $_gitworktree'. Such
a change of the current directory is not necessary, because

- if we have a working tree, then the startup routine has already
  changed the current directory to the root of the working tree, which
  *is* $_gitworktree; or

- if we are in a bare repository, then there is no point in changing
  the current directory anywhere. (And $_gitworktree is empty.)

Signed-off-by: Johannes Sixt <redacted>
Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agogit-gui: use HEAD as current branch when detached
Mark Levedahl [Sun, 31 May 2026 23:02:14 +0000 (19:02 -0400)]
git-gui: use HEAD as current branch when detached

commit f87a36b697 ("git-gui: use git-branch --show-current", 2024-02-12)
changed git-gui to use git-branch to access refs, rather than directly
reading files as doing the latter is not compatible with the reftable
backend. git branch --show-current reports an empty branch name when the
head is detached, and in this case load_current_branch needs to report
HEAD using special case logic as it did prior to the above commit. Make
it do so.

This addresses an issue with git-gui browser failing with a detached
head.

Signed-off-by: Mark Levedahl <redacted>
Signed-off-by: Johannes Sixt <redacted>
2 months agoThe 10th batch
Junio C Hamano [Sun, 31 May 2026 01:00:11 +0000 (10:00 +0900)]
The 10th batch

Signed-off-by: Junio C Hamano <redacted>
2 months agoMerge branch 'sp/doc-range-diff-takes-notes'
Junio C Hamano [Sun, 31 May 2026 01:00:39 +0000 (10:00 +0900)]
Merge branch 'sp/doc-range-diff-takes-notes'

Docfix.

* sp/doc-range-diff-takes-notes:
  Documentation/git-range-diff: add missing notes options in synopsis

2 months agoMerge branch 'ps/gitlab-ci-macOS-improvements'
Junio C Hamano [Sun, 31 May 2026 01:00:38 +0000 (10:00 +0900)]
Merge branch 'ps/gitlab-ci-macOS-improvements'

Update GitLab CI jobs that exercise macOS.

* ps/gitlab-ci-macOS-improvements:
  gitlab-ci: update macOS image
  gitlab-ci: upgrade macOS runners

2 months agoMerge branch 'sa/cat-file-batch-mailmap-switch'
Junio C Hamano [Sun, 31 May 2026 01:00:38 +0000 (10:00 +0900)]
Merge branch 'sa/cat-file-batch-mailmap-switch'

"git cat-file --batch" learns an in-line command "mailmap"
that lets the user toggle use of mailmap.

* sa/cat-file-batch-mailmap-switch:
  cat-file: add mailmap subcommand to --batch-command

2 months agoMerge branch 'jk/commit-graph-lazy-load-fallback'
Junio C Hamano [Sun, 31 May 2026 01:00:38 +0000 (10:00 +0900)]
Merge branch 'jk/commit-graph-lazy-load-fallback'

The logic to lazy-load trees from the commit-graph has been made
more robust by falling back to reading the commit object when
the commit-graph is no longer available.

* jk/commit-graph-lazy-load-fallback:
  commit: fall back to full read when maybe_tree is NULL

2 months agoMerge branch 'pt/fsmonitor-linux'
Junio C Hamano [Sun, 31 May 2026 01:00:38 +0000 (10:00 +0900)]
Merge branch 'pt/fsmonitor-linux'

The fsmonitor daemon has been implemented for Linux.

* pt/fsmonitor-linux:
  fsmonitor: convert shown khash to strset in do_handle_client
  fsmonitor: add tests for Linux
  fsmonitor: add timeout to daemon stop command
  fsmonitor: close inherited file descriptors and detach in daemon
  run-command: add close_fd_above_stderr option
  fsmonitor: implement filesystem change listener for Linux
  fsmonitor: rename fsm-settings-darwin.c to fsm-settings-unix.c
  fsmonitor: rename fsm-ipc-darwin.c to fsm-ipc-unix.c
  fsmonitor: use pthread_cond_timedwait for cookie wait
  compat/win32: add pthread_cond_timedwait
  fsmonitor: fix hashmap memory leak in fsmonitor_run_daemon
  fsmonitor: fix khash memory leak in do_handle_client
  t9210, t9211: disable GIT_TEST_SPLIT_INDEX for scalar clone tests

2 months agoMerge branch 'ps/graph-lane-limit'
Junio C Hamano [Sun, 31 May 2026 01:00:37 +0000 (10:00 +0900)]
Merge branch 'ps/graph-lane-limit'

The graph output from commands like "git log --graph" can now be
limited to a specified number of lanes, preventing overly wide output
in repositories with many branches.

* ps/graph-lane-limit:
  graph: add truncation mark to capped lanes
  graph: add --graph-lane-limit option
  graph: limit the graph width to a hard-coded max

2 months agoMerge branch 'jr/bisect-custom-terms-in-output'
Junio C Hamano [Sun, 31 May 2026 01:00:37 +0000 (10:00 +0900)]
Merge branch 'jr/bisect-custom-terms-in-output'

"git bisect" now uses the selected terms (e.g., old/new) more
consistently in its output.

* jr/bisect-custom-terms-in-output:
  rev-parse: use selected alternate terms to look up refs
  bisect: print bisect terms in single quotes
  bisect: use selected alternate terms in status output

2 months agoMerge branch 'tc/generate-configlist-fix-for-older-ninja'
Junio C Hamano [Sun, 31 May 2026 01:00:37 +0000 (10:00 +0900)]
Merge branch 'tc/generate-configlist-fix-for-older-ninja'

Build update.

* tc/generate-configlist-fix-for-older-ninja:
  generate-configlist: collapse depfile for older Ninja

2 months agoMerge branch 'kk/tips-reachable-from-bases-optim'
Junio C Hamano [Sun, 31 May 2026 01:00:37 +0000 (10:00 +0900)]
Merge branch 'kk/tips-reachable-from-bases-optim'

Revision traversal optimization.

* kk/tips-reachable-from-bases-optim:
  t6600: add tests for duplicate tips in tips_reachable_from_bases()
  commit-reach: use object flags for tips_reachable_from_bases()

2 months agoMerge branch 'ed/check-connected-close-err-fd'
Junio C Hamano [Sun, 31 May 2026 01:00:36 +0000 (10:00 +0900)]
Merge branch 'ed/check-connected-close-err-fd'

File descriptor leak fix.

* ed/check-connected-close-err-fd:

2 months agoMerge branch 'ed/check-connected-close-err-fd-2.53'
Junio C Hamano [Sun, 31 May 2026 01:00:36 +0000 (10:00 +0900)]
Merge branch 'ed/check-connected-close-err-fd-2.53'

File descriptor leak fix (for 2.54 maintenance track).

* ed/check-connected-close-err-fd-2.53:
  connected: close err_fd in promisor fast-path

2 months agodaemon: guard NULL REMOTE_PORT in execute() logging
Sebastien Tardif [Thu, 28 May 2026 02:56:56 +0000 (02:56 +0000)]
daemon: guard NULL REMOTE_PORT in execute() logging

REMOTE_ADDR and REMOTE_PORT are both set by the same code path in
handle(), so when the existing REMOTE_ADDR check passes, REMOTE_PORT
is guaranteed to be non-NULL.  Guard REMOTE_PORT as well so that a
future change that breaks this invariant does not pass NULL to
printf's %s, which is undefined behavior.

Signed-off-by: Sebastien Tardif <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agodaemon: fix IPv6 address truncation in ip2str()
Sebastien Tardif [Thu, 28 May 2026 02:56:55 +0000 (02:56 +0000)]
daemon: fix IPv6 address truncation in ip2str()

The sockaddr struct size (ai_addrlen) is passed as the output buffer
size to inet_ntop(). For IPv6, sizeof(sockaddr_in6) is 28 bytes but
INET6_ADDRSTRLEN is 46, so long IPv6 addresses are silently truncated.

Fix this by passing sizeof(ip) instead, which is the actual size of
the destination buffer. Drop the now-unused len parameter from
ip2str() and update all callers.

Signed-off-by: Sebastien Tardif <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agodaemon: fix IPv6 address corruption in lookup_hostname()
Sebastien Tardif [Thu, 28 May 2026 02:56:54 +0000 (02:56 +0000)]
daemon: fix IPv6 address corruption in lookup_hostname()

getaddrinfo() is called with AF_UNSPEC hints, so it may return IPv6
results. However, the code unconditionally casts ai_addr to
sockaddr_in and passes AF_INET to inet_ntop(). On IPv6-only hosts,
this reads from the wrong struct offset, producing garbage IP
addresses.

Fix this by checking ai_family and extracting the address pointer
into a local variable before calling inet_ntop() once with the
correct family. Die on unexpected address families.

Signed-off-by: Sebastien Tardif <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoline-log: allow non-patch diff formats with -L
Michael Montalbo [Thu, 28 May 2026 20:47:46 +0000 (20:47 +0000)]
line-log: allow non-patch diff formats with -L

Now that -L flows through log_tree_diff_flush() and diff_flush(),
metadata-only diff formats work because they only read filepair
fields (status, mode, path, oid) already set on the pre-computed
pairs.

Expand the allowlist in setup_revisions() to also accept --raw,
--name-only, --name-status, and --summary.  Diff stat formats
(--stat, --numstat, --shortstat, --dirstat) remain blocked because
they call compute_diffstat() on full blob content and would show
whole-file statistics rather than range-scoped ones.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agoline-log: integrate -L output with the standard log-tree pipeline
Michael Montalbo [Thu, 28 May 2026 20:47:45 +0000 (20:47 +0000)]
line-log: integrate -L output with the standard log-tree pipeline

`git log -L` has bypassed log_tree_diff() and log_tree_diff_flush()
since the feature was introduced, short-circuiting from
log_tree_commit() directly into line_log_print().  This skips the
no_free save/restore (noted in a NEEDSWORK comment added by
f8781bfda3), the always_show_header fallback, show_diff_of_diff(),
and diff_free() cleanup.

Restructure so that -L flows through log_tree_diff() ->
log_tree_diff_flush(), the same path used by the normal
single-parent and merge diff codepaths:

 - Rename line_log_print() to line_log_queue_pairs() and strip it
   down to just queuing pre-computed filepairs.  The show_log(),
   separator, diffcore_std(), and diff_flush() calls are removed
   since log_tree_diff_flush() handles all of those.

 - In log_tree_diff(), call line_log_queue_pairs() then
   log_tree_diff_flush(), mirroring the diff_tree_oid() + flush
   pattern used by the single-parent and merge codepaths.

 - Remove the early return in log_tree_commit() that is no longer
   needed now that -L output flows through log_tree_diff() and
   log_tree_diff_flush(); this restores no_free save/restore,
   always_show_header, and diff_free() cleanup.

Because show_log() is now deferred until after diffcore_std() inside
log_tree_diff_flush(), pickaxe (-S, -G, --find-object) and
--diff-filter now properly suppress commits when all pairs are
filtered out.

The blank-line separator between commit header and diff changes
slightly: the old code printed one unconditionally, while
log_tree_diff_flush() only emits one for verbose headers.  This
matches the rest of log output.

Also reject --full-diff, which is not yet supported with -L: the
filepairs are pre-computed during the history walk and scoped to
tracked line ranges, so there is currently no full-tree diff to
fall back to for display.

Update tests accordingly.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agorevision: move -L setup before output_format-to-diff derivation
Michael Montalbo [Thu, 28 May 2026 20:47:44 +0000 (20:47 +0000)]
revision: move -L setup before output_format-to-diff derivation

The line_level_traverse block sets a default DIFF_FORMAT_PATCH when
no output format has been explicitly requested.  This default must
be visible to the "Did the user ask for any diff output?" check
that derives revs->diff from revs->diffopt.output_format.

Currently the -L block runs after that derivation, so revs->diff
stays 0 when no explicit format is given.  This does not matter yet
because log_tree_commit() short-circuits into line_log_print()
before consulting revs->diff, but the next commit will route -L
through the normal log_tree_diff() path, which checks revs->diff.

Move the block above the derivation so the default DIFF_FORMAT_PATCH
is in place when revs->diff is computed.  No behavior change on its
own.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agodoc: clarify that --word-diff operates on line-level hunks
Michael Montalbo [Thu, 28 May 2026 19:21:45 +0000 (19:21 +0000)]
doc: clarify that --word-diff operates on line-level hunks

The --word-diff documentation describes the output modes and
word-regex mechanics but does not explain that word-diff operates
within the hunks produced by the line-level diff rather than
performing an independent word-stream comparison.  This can
surprise users when the line-level alignment causes word-level
changes to appear even though the words in both files are
identical.

Add an implementation note explaining the two-stage relationship
and that the output may change if Git acquires a different
implementation in the future.

Signed-off-by: Michael Montalbo <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agot3070: skip ls-files tests with backslash patterns on Windows
Kristofer Karlsson [Thu, 28 May 2026 09:00:48 +0000 (09:00 +0000)]
t3070: skip ls-files tests with backslash patterns on Windows

On Windows (MINGW), backslashes in pathspecs are silently converted to
forward slashes (directory separators), which changes the glob semantics.
This causes 36 test failures in t3070-wildmatch when the "via ls-files"
variants test patterns containing backslash escapes (e.g. '\[ab]',
'[\-_]', '[A-\\]').

The wildmatch function itself handles these patterns correctly — only the
ls-files code path fails because pathspec parsing converts the
backslashes before they reach the glob matcher.

Skip these ls-files tests on platforms where BSLASHPSPEC is not set,
which is the existing prereq that captures exactly this semantic:
"backslashes in pathspec are not directory separators."

Signed-off-by: Kristofer Karlsson <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agocommit: remove deprecated functions
Kristoffer Haugsbakk [Thu, 28 May 2026 07:00:11 +0000 (09:00 +0200)]
commit: remove deprecated functions

These functions were deprecated in a series of commits merged in
52882024 (Merge branch 'ps/commit-list-functions-renamed', 2026-02-13).

The compatibility was for in-flight topics at the time.

Acked-by: Patrick Steinhardt <redacted>
Signed-off-by: Kristoffer Haugsbakk <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months ago*: replace deprecated free_commit_list
Kristoffer Haugsbakk [Thu, 28 May 2026 07:00:10 +0000 (09:00 +0200)]
*: replace deprecated free_commit_list

Replace `free_commit_list` with `commit_list_free`. The former was
deprecated in 9f18d089 (commit: rename `free_commit_list()` to conform
to coding guidelines, 2026-01-15).

This allows us to remove all the deprecated functions in the
next commit:

• `copy_commit_list`
• `reverse_commit_list`
• `free_commit_list`

Acked-by: Patrick Steinhardt <redacted>
Signed-off-by: Kristoffer Haugsbakk <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agopack-bitmap: build pseudo-merge bitmaps after regular bitmaps
Taylor Blau [Wed, 27 May 2026 19:56:11 +0000 (15:56 -0400)]
pack-bitmap: build pseudo-merge bitmaps after regular bitmaps

When generating bitmaps, `bitmap_builder_init()` starts with an initial
selection of commits to receive bitmap coverage, and then determines a
set of "maximal" commits based on its input.

Commit 089f751360f (pack-bitmap-write: build fewer intermediate bitmaps,
2020-12-08) has extensive details, but the gist is as follows:

Each selected commit starts with one commit_mask bit in its "commit
mask" bitmap. Then, we walk the first-parent history in topological
order and OR each commit's mask into its (first) parent. Whenever that
OR results in the parent having more bits set, the child is deemed to be
non-maximal, and the frontier is pushed further back along the first
parent history.

That approach works extremely well for ordinary selected commits, whose
first-parent histories often describe real sharing between the bitmaps
we are going to write.

It struggles, however, to efficiently generate pseudo-merge bitmaps.
Unlike ordinary commits for which the above algorithm is designed,
pseudo-merges don't represent any "real" commit in history, just a
grouping of non-bitmapped reference tips. In that sense, their first
parent is just a part of a larger set, and treating them like ordinary
selected commits imposes a significant slow-down when generating bitmaps
with pseudo-merges enabled.

Consider partitioning all non-bitmapped reference tips into eight
individual pseudo-merges via the following configuration:

    [bitmapPseudoMerge "all"]
        pattern=refs/
        threshold=now
        stableSize=10000000
        maxMerges=8

, the cost of generating a bitmap from scratch rises significantly:

    +------------------+-----------------+---------------+---------------------+
    |                  | no pseudo-merge | pseudo-merges | Delta               |
    |                  |                 | (HEAD^)       |                     |
    +------------------+-----------------+---------------+---------------------+
    | elapsed          |   294.1 s       |   575.0 s     |   +280.9 s (+95.5%) |
    | cycles           | 1,365.5 B       | 2,686.9 B     | +1,321.4 B (+96.8%) |
    | instructions     | 1,389.8 B       | 2,546.6 B     | +1,156.8 B (+83.2%) |
    | CPI              |     0.983       |     1.055     |   +0.073    (+7.4%) |
    +------------------+-----------------+---------------+---------------------+

This is a particularly poor trade-off, because the time saved by these
pseudo-merges during, e.g.,

    $ git rev-list --count --all --objects --use-bitmap-index

is only:

    $ hyperfine -L v true,false -n 'pseudo-merges: {v}' '
        GIT_TEST_USE_PSEUDO_MERGES={v} git.compile rev-list --count \
          --objects --all --use-bitmap-index
      '

    Benchmark 1: pseudo-merges: true
      Time (mean ± σ):      2.613 s ±  0.012 s    [User: 2.308 s, System: 0.305 s]
      Range (min … max):    2.594 s …  2.633 s    10 runs

    Benchmark 2: pseudo-merges: false
      Time (mean ± σ):     52.205 s ±  0.170 s    [User: 51.500 s, System: 0.697 s]
      Range (min … max):   51.956 s … 52.458 s    10 runs

    Summary
      pseudo-merges: true ran
       19.98 ± 0.11 times faster than pseudo-merges: false

In other words, we pay a nearly ~5 minute penalty to generate
pseudo-merge bitmaps, but only save ~50 seconds during traversal.

The problem stems from injecting pseudo-merges into the bitmap builder
as if they were normal commits. The maximal commit selection algorithm
was simply not designed for that case, and performs predictably poorly.

The only reason we reused the maximal commit selection routine for
pseudo-merges alongside regular non-pseudo-merge commits is because we
represent them both as commit objects (where the pseudo-merge commits
just represent a made-up commit as opposed to one that actually exists
in a repository's object store).

Instead, build the regular selected commit bitmaps first, considering
only non-pseudo-merge commits in `bitmap_builder_init()`. Once those
bitmaps have been stored, build each pseudo-merge bitmap separately and
attach its parent and object bitmaps to the corresponding pseudo-merge
entry before writing the extension.

This keeps the regular bitmap build shaped like the no-pseudo-merge
case. The later pseudo-merge fill can still stop at stored selected
ancestor bitmaps, so it does not have to rewalk each pseudo-merge
closure from scratch.

When an existing bitmap has the same pseudo-merge parent set, reuse and
remap that whole pseudo-merge bitmap before falling back to
fill_bitmap_commit(). This preserves the benefit of stable pseudo-merges
while keeping the on-disk format and reader behavior unchanged.

As a result, the overhead cost for generating pseudo-merges in the above
configuration is much smaller:

    +------------------+-----------------+---------------+-------------------+
    |                  | no pseudo-merge | pseudo-merges | Delta             |
    |                  |                 | (HEAD)        |                   |
    +------------------+-----------------+---------------+-------------------+
    | elapsed          |   294.1 s       |   328.4 s     |  +34.3 s (+11.7%) |
    | cycles           | 1,365.5 B       | 1,529.3 B     | +163.7 B (+12.0%) |
    | instructions     | 1,389.8 B       | 1,552.8 B     | +163.0 B (+11.7%) |
    | CPI              |     0.983       |     0.985     |  +0.002   (+0.2%) |
    +------------------+-----------------+---------------+-------------------+

Recall that at the start of this series, generating reachability bitmaps
took 612.5 seconds *without* pseudo-merges. With this commit, it is
still ~46.38% *faster* to generate reachability bitmaps *with*
pseudo-merges than it was to generate bitmaps wihtout them at the
beginning of this series.

The changes to implement this are mostly straightforward. We exclude
pseudo-merge commits from the existing bitmap generation, and walk over
them in a separate pass, by either reusing an existing on-disk
pseudo-merge, or passing the pseudo-merge commit itself back to the
existing routine in `fill_bitmap_commit()`.

(Note that the routine to build pseudo-merge bitmaps is the same both
before and after this change, the difference is only that we do not let
psuedo-merges participate in determining the set of maximal commits.)

The only wrinkle is that `fill_bitmap_commit()` must be taught to not
expect that all tree objects have been parsed, which is the case for any
portion of history reachable by one or more pseudo-merge(s), but not by
any non-pseudo-merge commit selected for bitmapping.

Signed-off-by: Taylor Blau <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agopack-bitmap: remember pseudo-merge parents
Taylor Blau [Wed, 27 May 2026 19:56:08 +0000 (15:56 -0400)]
pack-bitmap: remember pseudo-merge parents

write_pseudo_merges() currently builds an array of temporary bitmaps for
the parent set of each pseudo-merge, then serializes those bitmaps later
while writing the extension.

Move those parent bitmaps onto the corresponding bitmapped_commit
entries instead. This keeps the on-disk output unchanged, but gives the
parent bitmap the same lifetime and access pattern that later changes
will use when pseudo-merge object bitmaps are built before the write
step.

Signed-off-by: Taylor Blau <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agopack-bitmap: sort bitmaps before XORing
Taylor Blau [Wed, 27 May 2026 19:56:05 +0000 (15:56 -0400)]
pack-bitmap: sort bitmaps before XORing

Reachability bitmaps may be stored as XORs against nearby bitmaps, up to
10 away. However, when callers provide selected commits in an arbitrary
order, the writer may miss good ancestor/descendant pairs and produce
much larger bitmap files without changing query coverage.

Sort the selected bitmaps in date order (from oldest to newest) before
computing XOR offsets, leaving pseudo-merge bitmaps alone (which we will
deal with separately in following commits).

On our same testing repository from previous commits, this change shrunk
our selection of 1,261 bitmaps from ~635.46 MiB to 176.4 MiB for a
~72.24% reduction in the on-disk size of our *.bitmap file. The time to
generate the smaller bitmap file decreased by ~3.69 seconds, though this
is likely mostly noise.

Signed-off-by: Taylor Blau <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agopack-bitmap: cache object positions during fill
Taylor Blau [Wed, 27 May 2026 19:56:02 +0000 (15:56 -0400)]
pack-bitmap: cache object positions during fill

The previous commits removed some redundant work from bitmap generation
by avoiding unnecessary tree recursion and by reusing selected bitmaps
that have already been computed.

Even with those changes in place, there is still an extremely hot path
from `fill_bitmap_commit()` and `fill_bitmap_tree()` to translate object
IDs into their corresponding bit positions in order to generate their
bitmaps.

In a small repository, this overhead is not significant. However, in a
very large repository (e.g., the one that we have been using as a
benchmark over the past several commits with ~57M total objects), the
overhead of locating object bit positions (often repeatedly) adds up
significantly.

Combat this by adding a small, direct-mapped cache to the bitmap writer
which maps object IDs to their corresponding bit positions. Size the
cache according to the number of objects being written, with fixed lower
and upper bounds so small repositories do not pay for a large table and
large repositories can avoid most repeated packlist and MIDX lookups.

On my machine with (a somewhat outdated) GCC 15.2.0, each entry in the
cache is 40 bytes wide:

    $ pahole -C bitmap_pos_cache_entry pack-bitmap-write.o
    struct bitmap_pos_cache_entry {
            struct object_id           oid;                  /*     0    36 */
            uint32_t                   pos;                  /*    36     4 */

            /* size: 40, cachelines: 1, members: 2 */
            /* last cacheline: 40 bytes */
    };

, and we will allocate up to 2^21 entries for a maximum total of 80 MiB
of cache overhead.

In our example repository from above and in earlier commits, this
results in a ~9.4% reduction in runtime relative to the previous commit:

    +------------------+-------------+-------------+---------------------+
    |                  | HEAD^       | HEAD        | Delta               |
    +------------------+-------------+-------------+---------------------+
    | elapsed          |   324.8 s   |   294.1 s   |    -30.7 s  (-9.4%) |
    | cycles           | 1,508.6 B   | 1,365.5 B   |   -143.0 B  (-9.5%) |
    | instructions     | 1,436.6 B   | 1,389.8 B   |    -46.9 B  (-3.3%) |
    | CPI              |     1.050   |     0.983   |   -0.068    (-6.4%) |
    +------------------+-------------+-------------+---------------------+

When generating bitmaps on this repository (to produce the above
timings), the cache grew to its maximum size of 80 MiB, and resulted in
1.024B cache hits and 59.957M cache misses.

Signed-off-by: Taylor Blau <redacted>
Signed-off-by: Junio C Hamano <redacted>
2 months agopack-bitmap: consolidate `find_object_pos()` success path
Taylor Blau [Wed, 27 May 2026 19:55:59 +0000 (15:55 -0400)]
pack-bitmap: consolidate `find_object_pos()` success path

Both sides of `find_object_pos()` report success in the same way by
setting the optional `found` out-parameter and return the resolved
bitmap position.

Prepare for adding more bookkeeping around object-position lookups by
storing the result in a local `pos` variable and sharing the success
return path between the packlist and MIDX cases.

Signed-off-by: Taylor Blau <redacted>
Signed-off-by: Junio C Hamano <redacted>
git clone https://git.99rst.org/PROJECT