Commit Graph

1150 Commits (ac7773455aee622228dd2f742487bc8bb58d043c)

Author SHA1 Message Date
Junio C Hamano ac7773455a Merge branch 'mm/line-log-limited-ops' into seen
The 'git log -L<range>:<path>' command has been taught to limit
various 'diff' operations, such as '--stat', '--check', and '-G', to
the specified range and path.

* mm/line-log-limited-ops:
  diffcore-pickaxe: scope -G to the -L tracked range
  diff: support --check with -L line ranges
  line-log: support diff stat formats with -L
  diff: extract a line-range diff helper for reuse
  diff: emit -L hunk headers via xdiff's formatter
  diff: simplify the line-range filter by classifying removals immediately
  diff: rename and group the line-range filter for clarity
2026-08-31 13:53:25 -07:00
Tian Yuchen 508ec9837c repository: move fetch_if_missing into struct repository
The global variable 'fetch_if_missing' controls whether a missing
object check should prompt a lazy fetch from a promisor remote.
In order to continue the libification effort, move it into
'struct repository' and initialize it to 1 by default to keep the
previous behavior.

builtin/fetch-pack.c, builtin/fsck.c, and builtin/rev-list.c are
entered via commands marked RUN_SETUP in git.c:commands[]. Their
'repo' parameter is only NULL when '-h' is given outside of a
repository, in which case either show_usage_if_asked() or
parse_options()'s own '-h' handling exits the process before
returning. We can therefore drop their UNUSED markers and assign
to 'repo' directly.

builtin/index-pack.c is entered via RUN_SETUP_GENTLY, so its
'repo' pointer can be NULL any time it is run outside of a
repository, not only with '-h'. We keep a NULL check there and fall
back to 'the_repository'.

builtin/pack-objects.c needs two adjustments to make 'repo' reach
every 'fetch_if_missing' call site: 'read_stdin_packs()' now takes a
'struct repository *'; 'option_parse_missing_action()', which
is registered as an OPT_CALLBACK, receives a 'repo' through the
option's 'value' field now.

Additionally, update the partial clone documentation to reflect
that this is now a per-repository flag.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Ayush Chandekar <ayu.chandekar@gmail.com>
Mentored-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Tian Yuchen <cat@malon.dev>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-08-15 08:37:54 -07:00
Junio C Hamano ef11815b1e Merge branch 'mm/revision-pure-get-commit-action'
The get_commit_action() function has been refactored to be a pure
predicate by moving the side-effecting line-level log range folding to
simplify_commit().  This ensures that evaluating a commit's action
before the walk reaches it does not prematurely mutate its tracked
line ranges, making it safer for potential lookahead evaluations.

* mm/revision-pure-get-commit-action:
  revision: make get_commit_action() a pure predicate
2026-08-07 14:48:00 -07:00
Junio C Hamano e2753febd9 Merge branch 'jc/exclude-first-parent-seen'
Traversals with '--exclude-first-parent-only' have been corrected
to properly stop after the first parent even when it has already
been marked as 'SEEN'.

* jc/exclude-first-parent-seen:
  revision: honor --exclude-first-parent-only with SEEN first parent
2026-08-03 09:31:29 -07:00
Junio C Hamano 0c20b08631 Merge branch 'ps/refs-wo-the-repository'
The ref subsystem and the worktree API have been refactored to pass a
repository pointer down the call chain, allowing them to drop
references to the global 'the_repository' variable.  As part of this,
the handling of the 'core.packedRefsTimeout' configuration has been
moved into the per-repository ref store structure.

* ps/refs-wo-the-repository:
  refs: remove remaining uses of `the_repository`
  worktree: pass repository to public functions
  worktree: pass repository to file-local functions
  worktree: refactor code to use available repositories
  refs/files: drop `USE_THE_REPOSITORY_VARIABLE`
  refs/packed: de-globalize handling of "core.packedRefsTimeout"
2026-07-27 09:08:59 -07:00
Junio C Hamano fbf123a028 Merge branch 'ps/shift-root-in-graph'
'git log --graph' has been modified to visually distinguish parentless
'root' commits (and commits that become roots due to history
simplification) by indenting them, preventing them from appearing
falsely related to unrelated commits rendered immediately above them.

* ps/shift-root-in-graph:
  graph: add --[no-]graph-indent and log.graphIndent
  graph: move config reading into graph_read_config()
  graph: wrap cascading commits after 4 columns
  graph: indent visual root in graph
  graph: add a 2 commit buffer for lookahead
  revision: add next_commit_to_show()
  lib-log-graph: move check_graph function
2026-07-27 09:08:58 -07:00
Junio C Hamano 47382f7398 revision: honor --exclude-first-parent-only with SEEN first parent
The '--exclude-first-parent-only' option instructs the revision
walker to follow only the first parent of a merge commit to
propagate down the UNINTERESTING bit.

However, if the first parent has already been marked SEEN (for
example, because it was explicitly specified on the command line),
process_parents() skips it with a 'continue' statement.  But the
loop then continues on to process the second parent, because the
check for the '--exclude-first-parent-only' option is near the end
of the loop, which the 'continue' statement skips.  Consequently, we
end up marking the second parent as UNINTERESTING.

Break out of the loop instead of continuing when the first parent is
already SEEN or fails to parse.  This ensures that we do not process
subsequent parents and mark them as UNINTERESTING.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
Reviewed-by: Jerry Zhang <jerry@skydio.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-22 14:00:02 -07:00
Junio C Hamano 60aa81f2ef Merge branch 'kk/no-walk-pathspec-fix'
The 'git rev-list --no-walk' command has been corrected to restore
pathspec filtering, which was lost when the streaming walk was
refactored.

* kk/no-walk-pathspec-fix:
  revision: fix --no-walk path filtering regression
2026-07-22 10:30:55 -07:00
Junio C Hamano 0b75ef5168 Merge branch 'js/coverity-fixes-null-safety'
Various code paths have been hardened against potential NULL-pointer
dereferences and invalid file descriptor accesses flagged by
Coverity.

* js/coverity-fixes-null-safety:
  shallow: give write_one_shallow() its own hex buffer
  shallow: fix NULL dereference
  bisect: ensure non-NULL `head` before using it
  pack-bitmap: handle missing bitmap for base MIDX
  revision: avoid dereferencing NULL in `add_parents_only()`
  replay: die when --onto does not peel to a commit
  bisect: handle NULL commit in `bisect_successful()`
  mailsplit: move NULL check before first use of file handle
  reftable/stack: guard against NULL list_file in stack_destroy
  remote: guard `remote_tracking()` against NULL remote
  diff: handle NULL return from repo_get_commit_tree()
  diffcore-break: guard against NULLed queue entries in merge loop
2026-07-19 10:42:20 -07:00
Kristofer Karlsson 32c4ed70e2 revision: fix --no-walk path filtering regression
Since dd4bc01c0a (revision: use priority queue for non-limited
streaming walks, 2026-05-27), "git rev-list --no-walk <commit>
-- <path>" ignores the path arguments and outputs all commits
regardless of whether they touch the given paths.

That commit introduced a REV_WALK_NO_WALK enum value to separate
--no-walk from the streaming walk in get_revision_1(). The new
case skips process_parents(), which is correct for not enqueuing
parents, but also skips try_to_simplify_commit() which
process_parents() calls to evaluate whether each commit touches
the given paths.

Add a call to try_to_simplify_commit() for the
REV_WALK_NO_WALK case, folding it into the existing
REV_WALK_REFLOG case which already does the same.

Add tests for --no-walk path filtering to t6017. The
"single commit, match" test is defensive and passes without
the fix, while the other two fail without it.

Reported-by: Peter Colberg <pcolberg@redhat.com>
Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-16 10:53:38 -07:00
Patrick Steinhardt e2533e0915 worktree: pass repository to public functions
Refactor remaining public functions that still depend on
`the_repository` to instead receive a repository as parameter. This
allows us to get rid of `USE_THE_REPOSITORY_VARIABLE`.

Adapt callers accordingly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-16 07:40:22 -07:00
Michael Montalbo da0fb7e36e revision: make get_commit_action() a pure predicate
get_commit_action() reads as a predicate that decides whether a commit
is shown or ignored, but for a line-level log without parent rewriting
it also calls line_log_process_ranges_arbitrary_commit(), which
mutates the tracked line ranges.  That hidden side effect makes it unsafe
to evaluate ahead of the walk, the way a lookahead would.

get_commit_action() was split out of simplify_commit() in beb5af43a6
(graph API: fix bug in graph_is_interesting(), 2009-08-18) as the
show/ignore decision minus the parent rewriting, so the graph renderer
could reuse it; line-level log later routed its filtering through it as
well, in 3cb9d2b6 (line-log: more responsive, incremental 'git log -L',
2020-05-11).  Besides simplify_commit(), the walk driver,
graph_is_interesting() is its only other caller, and it runs only under
--graph, which sets rewrite_parents and therefore want_ancestry(); the
"-L without ancestry" branch that holds the side effect never fires
there, so it is dormant today.

The line-level processing folds a commit's tracked ranges onto its
parents, which must happen even for a commit that get_commit_action()
filters from the output, or the ranges never reach the parents.  Move it
to simplify_commit() and run it before get_commit_action(), gated by
get_commit_action()'s leading checks (already shown, uninteresting, and
the like) so a commit ignored by those is not folded, as before; factor
those checks out as commit_early_ignore().  get_commit_action() is then
side-effect free.

commit_early_ignore() runs twice on the -L path, once for that gate and
once inside get_commit_action(), but it reads only object flags and pack
membership, disjoint from the TREESAME flag the fold sets, so the repeat
is harmless.

Add a "line-log-peek" subcommand to the revision-walking test helper
that evaluates get_commit_action() on a commit the walk has not reached
yet, plus a t4211 check that the call leaves the commit's flags
unchanged.  The flags are compared rather than the commit list because
add_line_range() merges ranges by union, which is idempotent, so the
side effect never changed which commits a linear -L history shows.

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-15 14:22:46 -07:00
Junio C Hamano 0a4fb3105e Merge branch 'jk/bloom-leak-fixes'
Various memory leaks in the Bloom-filter code paths that are exposed
when running tests with the 'GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1'
environment variable have been plugged.

* jk/bloom-leak-fixes:
  line-log: drop extra copy of range with bloom filters
  revision: avoid leaking bloom keyvecs with multiple traversals
  bloom: make bloom-filter slab initialization idempotent
2026-07-15 13:24:19 -07:00
Pablo Sabater e9b5720bef graph: add --[no-]graph-indent and log.graphIndent
Some users may prefer to not have graph indentation.

Add "log.graphIndent" config variable to graph_read_config() to read the
default preference. By default is graph indentation is true.

Add --graph-indent and --no-graph-indent options to overwrite the
default preference.

Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-14 07:32:51 -07:00
Pablo Sabater bf3c696b44 graph: add a 2 commit buffer for lookahead
In a subsequent commit the graph renderer needs to know if the next
commit is a visual root or if it is the last commit to be shown. This
requires peeking 2 commits ahead.

Commits are pre-fetched in get_revision() through next_commit_to_show()
where they are also marked as SHOWN, regardless the source they come
from.

Update graph_is_interesting() so it considers commits inside the
lookahead buffer as interesting as well.

Helped-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-14 07:32:51 -07:00
Pablo Sabater 08a0bef796 revision: add next_commit_to_show()
get_revision() gets its commits from two sources depending on the mode:

1. Normally it gets the commits from get_revision_internal().

2. --max-count-oldest which was introduced at bb4ce23284 (revision.c:
   implement --max-count-oldest, 2026-05-19) gets the commits by popping
   from a saved list at revs->commits marking SHOWN and CHILD_SHOWN on
   each popped commit.

Extract the choice logic into a helper, next_commit_to_show(), which
returns the next commit regardless of the source it comes from.

This has no change in behavior. The helper is needed in a subsequent
commit that pre-fetches two commits into a buffer for lookahead purposes
and needs to pre-fetch from the same source.

The --reverse branch keeps its own pop loop. Using the helper for
--reverse would additionally set SHOWN and CHILD_SHOWN which is not
desired and a behavior change.

Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-14 07:32:51 -07:00
Junio C Hamano 3c775abab0 Merge branch 'kk/prio-queue-get-put-fusion'
The lazy priority queue optimization pattern (deferring actual removal
in 'prio_queue_get()' to allow get+put fusion) has been folded
directly into 'prio_queue' itself, speeding up commit traversal
workflows and simplifying callers.

* kk/prio-queue-get-put-fusion:
  prio-queue: fold lazy_queue into prio_queue for automatic get+put fusion
  prio-queue: rename .nr to .nr_ and add accessor helpers
2026-07-13 08:27:27 -07:00
Johannes Schindelin f5887e1ec0 revision: avoid dereferencing NULL in `add_parents_only()`
This function resolves revision suffixes like commit^@ (all parents),
commit^! (commit minus parents), and commit^-N (exclude Nth parent). It
calls `get_reference()` in a loop to peel through tag objects until it
reaches a commit.

The existing NULL check after `get_reference()` only handles the
ignore_missing case, but get_reference() can return NULL through three
distinct paths:

  1. revs->ignore_missing: the caller asked to silently skip missing
     objects.

  2. revs->exclude_promisor_objects: the object is a lazy promisor
     object that should be excluded from the walk.

  3. revs->do_not_die_on_missing_objects: the caller wants to record
     missing OIDs for later reporting (used by `git rev-list
     --missing=print`) rather than dying.

In the latter two instances, the code falls through to dereference the
NULL pointer.

Handle all three cases explicitly:

  - ignore_missing: return 0, matching the existing behavior and
    the pattern in `handle_revision_arg()`.

  - do_not_die_on_missing_objects: return 0. The missing OID has already
    been recorded in `revs->missing_commits` by `get_reference()`.
    Returning 0 is consistent with `handle_revision_arg()` and
    `process_parents()`, both of which continue without error when this flag
    is set. The broader codebase pattern for this flag is "record and
    continue": list-objects.c, builtin/rev-list.c, and process_parents
    all skip the die/error and keep walking.

  - everything else (only the `exclude_promisor_objects` case in
    practice): return -1, consistent with `handle_revision_arg()` where
    the condition only matches `ignore_missing` or
    `do_not_die_on_missing_objects`, falling through to ret = -1 for the
    promisor case.

Note: the callers of `add_parents_only()` in
`handle_revision_pseudo_opt()` treat any nonzero return as "handled"
(`if (add_parents_only(...)) { ret = 0; }`), so the -1 for the promisor
case is indistinguishable from success there. This means a
promisor-excluded tag target referenced via commit^@ would be silently
skipped rather than producing an error.  This is a pre-existing
limitation of the caller's return value handling and not made worse by
this change; the alternative (a NULL dereference crash) _would be_
strictly worse.

Pointed out by Coverity.

Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-10 08:13:54 -07:00
Jeff King c34573e638 revision: avoid leaking bloom keyvecs with multiple traversals
In prepare_revision_walk(), we convert the pruning pathspecs into
bloom-filter "keyvecs" via prepare_to_use_bloom_filter(). This allocates
memory which is then freed eventually by release_revisions(), via
release_revisions_bloom_keyvecs().

But there's one case where we leak. If a caller uses the same rev_info
for multiple walks, calling prepare_revision_walk() multiple times, then
subsequent calls will overwrite the earlier keyvecs, leaking them. This
can happen with "git show foo bar", which does a separate no-walk
traversal for "foo" and "bar". Building with SANITIZE=leak and running
the test suite like:

  GIT_TEST_COMMIT_GRAPH=1 \
  GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1 \
  ./t4013-diff-various.sh

will trigger a complaint from LSan. It does not happen without those
extra flags because we don't store on-disk bloom filters by default, and
thus we optimize out the keyvec computation.

We can fix the leak by discarding the old entries before generating new
ones.

There's an alternative fix, which is that prepare_to_use_bloom_filter()
could notice that we already have keyvec entries and just reuse them.
But this is less safe; the keyvec depends on the pruning pathspec, and
we don't know if that has changed.

I think it would _probably_ work in practice, since any caller using a
rev_info for multiple traversals is probably doing so with the same
pathspec. But it would also create a very subtle bug if that assumption
is violated. So we'll do the safer thing here, and generate fresh keyvec
entries for each traversal. The efficiency difference is probably not
noticeable, and this is what was happening already (we just weren't
bothering to free the old ones!).

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-07-01 08:44:06 -07:00
Michael Montalbo 54438a56a4 diff: support --check with -L line ranges
builtin_checkdiff() runs its own xdiff pass to detect whitespace
errors in newly added lines.  When -L is active, the check should
be scoped to the tracked line ranges rather than the whole file.

Reuse the line_range_filter to wrap checkdiff_consume(), the same
pattern already used for patch output and diffstat.  The filter
forwards only in-range lines for whitespace checking.

checkdiff reports the file line number of each error, which it
normally learns from the hunk header via checkdiff_consume_hunk().
The filter synthesizes its own hunk headers, so give it an optional
hunk callback and route checkdiff_consume_hunk() through it; this
sets the post-image position before the in-range lines are replayed.
Without it the reported line numbers would count from the start of
the range hunk rather than the start of the file.

The trailing blank-at-eof check is a second pass that scans the whole
file via check_blank_at_eof(), so gate its report on the tracked
ranges as well; otherwise a blank line added at end of file is
reported even when it lies outside the range.

Add DIFF_FORMAT_CHECKDIFF to the -L output format allowlist in
setup_revisions() so that -L --check is accepted, and list --check
among the supported formats in the documentation.  Add tests covering
that whitespace errors are reported, scoped to the tracked range, and
labeled with the correct file line number, including when two errors
in one range are separated by a gap that would otherwise split into
multiple xdiff hunks.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-27 19:52:28 -07:00
Michael Montalbo 660aae7323 line-log: support diff stat formats with -L
Reuse the line_range_filter in builtin_diffstat() so the stat formats
count only the lines within the tracked range.  When a filepair carries
line_ranges, the filter wraps diffstat_consume() as its output callback,
forwarding only the lines inside the range for counting.
flush_range_hunk() replays buffered content through diffstat_consume(),
which ignores synthetic @@ headers since it only counts '+' and '-'
lines.

Expand the output format allowlist in setup_revisions() to accept
--stat, --numstat, and --shortstat with -L.

Leave --dirstat out of the allowlist so it is rejected like any other
unsupported format.  Its default mode counts each file's whole-file
byte damage via diffcore_count_changes(), outside the line-based
pipeline that the -L filter scopes, so bare --dirstat cannot honor the
tracked range.  The --dirstat=lines mode could: it aggregates the same
per-file line counts as --numstat, which -L already scopes.  But
accepting only that sub-mode while bare --dirstat keeps erroring is a
confusing split, so the whole format is deferred to a follow-up;
--numstat already reports the exact per-file counts within the tracked
range.

Also drop "yet" from the generic -L rejection message ("does not
yet support the requested diff format").  Some rejected formats do
not fit a line range at all, so "yet" wrongly implied they are all
just awaiting support.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-27 19:52:28 -07:00
Miklos Vajna 304812ed33 log: improve --follow following renames for non-linear history
Have a repo with a subtree merge, do a 'git log --follow prefix/test.c',
the output only contains history in the outer repo, not commits that
were merged via a subtree merge.

What happens is that 'git log --follow' stores the followed path only in
opt->diffopt.pathspec, so in case the commit history is non-linear, and
multiple parents have renames to the followed path, then the end result
isn't really defined: the first commit that happens to be visited in one
of the parents update opt->diffopt.pathspec, and from that point, only
that updated path is visited.

Fix the problem by introducing a commit -> path map
(follow_pathspec_slab) that stores what will be a path to follow when
visiting that parent. At the top of log_tree_commit(), if the slab has
an entry for this commit, we replace opt->diffopt.pathspec with a path
from this entry, so the correct path is followed, even if an unrelated
sub-tree changed the path to be followed to something else. After
log_tree_diff() runs, we record each parent's path in the slab. As a
result, the walk order doesn't matter, which was exactly the source of
problems previously.

This helps with subtree merges (rename happens inside the merge commit),
but also fixes the general case when the rename happens in the history
of parents, not in the merge commit itself.

Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-22 05:45:09 -07:00
Junio C Hamano 6e148f82dc Merge branch 'kk/streaming-walk-pqueue'
Streaming revision walks have been optimized by using a priority queue
for date-sorting commits, speeding up walks repositories with many
merges.

* kk/streaming-walk-pqueue:
  revision: use priority queue for non-limited streaming walks
  revision: introduce rev_walk_mode to clarify get_revision_1()
  pack-objects: call release_revisions() after cruft traversal
2026-06-16 09:01:02 -07:00
Junio C Hamano c534ec3a5d Merge branch 'mf/revision-max-count-oldest'
"git rev-list" (and "git log" family of commands) learned a new "--max-count-oldest"
that picks oldest N commits in the range instead of the usual newest.

* mf/revision-max-count-oldest:
  bash-completions: add --max-count-oldest
  revision.c: implement --max-count-oldest
2026-06-16 09:01:02 -07:00
Junio C Hamano 883a47ef64 Merge branch 'ob/more-repo-config-values'
Many core configuration variables have been migrated from global
variables into 'repo_config_values' to tie them to a specific
repository instance, avoiding cross-repository state leakage.

* ob/more-repo-config-values:
  environment: move "warn_on_object_refname_ambiguity" into `struct repo_config_values`
  environment: move "sparse_expect_files_outside_of_patterns" into `struct repo_config_values`
  environment: move "core_sparse_checkout_cone" into `struct repo_config_values`
  environment: move "precomposed_unicode" into `struct repo_config_values`
  environment: move "pack_compression_level" into `struct repo_config_values`
  environment: move `zlib_compression_level` into `struct repo_config_values`
  environment: move "check_stat" into `struct repo_config_values`
  environment: move "trust_ctime" into `struct repo_config_values`
2026-06-15 07:42:00 -07:00
Junio C Hamano 53ff393204 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
2026-06-11 04:31:17 -07:00
Kristofer Karlsson 3c57836988 prio-queue: rename .nr to .nr_ and add accessor helpers
Rename the .nr member to .nr_ so that callers outside prio-queue.c
that directly reference .nr get a compilation error.  This catches
both existing misuse and future in-flight topics.

Add prio_queue_size() for callers that need to know the element count
and prio_queue_for_each() for callers that need to walk all elements.

Convert all external .nr users:
 - Loop conditions: use prio_queue_size(), prio_queue_get(), or
   prio_queue_peek() as the loop condition
 - Array iterations: use prio_queue_for_each()

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-09 11:11:46 -07:00
Mirko Faina bb4ce23284 revision.c: implement --max-count-oldest
"--max-count" is a commit limiting option and sets a maximum amount
of commits to be shown. If a user wants to see only the first N
commits of the history (the oldest commits) they'd have to do
something like

    git log $(git rev-list HEAD | tail -n N | head -n 1)

This is not very user-friendly.

Teach get_revision() the --max-count-oldest option.

Signed-off-by: Mirko Faina <mroik@delayed.space>
[jc: fixed up t4202 <xmqq7boy4o05.fsf@gitster.g>]
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-03 08:37:52 +09:00
Olamide Caleb Bello 8407abf02a environment: move "warn_on_object_refname_ambiguity" into `struct repo_config_values`
The `core.warnAmbiguousRefs` configuration was previously stored in a
global `int` variable, making it shared across repository instances
and risking cross‑repository state leakage.

Store it instead in `repo_config_values`, where eagerly‑parsed
repository configuration lives. This option is parsed eagerly because
ambiguity warnings influence how users interpret object references in
many commands; a lazy parse could cause these warnings to behave
inconsistently or to appear for the wrong repository, confusing users
and hindering libification. This preserves the existing behavior while
tying the value to the repository from which it was read, avoiding
cross‑repository state leakage and continuing the effort to reduce
reliance on global configuration state.

Update all references to use `repo_config_values()`.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Usman Akinyemi <usmanakinyemi202@gmail.com>
Signed-off-by: Olamide Caleb Bello <belkid98@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-06-03 08:36:48 +09:00
Junio C Hamano 7af2503365 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
2026-05-31 10:00:38 +09:00
Michael Montalbo 4b5d8a0163 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 <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-29 14:06:21 +09:00
Michael Montalbo 42d960748e 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 <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-29 14:06:21 +09:00
Michael Montalbo 558057cf4f 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 <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-29 14:06:21 +09:00
Kristofer Karlsson dd4bc01c0a revision: use priority queue for non-limited streaming walks
The streaming (non-limited) walk in get_revision_1() inserts newly
discovered parent commits into a date-sorted queue via
commit_list_insert_by_date(), which scans the linked list to find the
insertion point -- O(w) per insert, where w is the width of the active
walk frontier.  Replace this with an O(log w) priority queue.

Add a commit_queue field to rev_info alongside the existing commits
linked list.  The two representations are mutually exclusive: setup
and external callers that need list access use the linked list, then
get_revision_1() lazily drains it into the priority queue on first
call.  Add a REV_WALK_NO_WALK enum value to distinguish the no_walk
case (which still uses the commit list) from the streaming case.

The conversion function rev_info_commit_list_to_queue() is public so
callers that know they will iterate can convert early.

Combined with the limit_list() priority queue change already in
master, this eliminates all O(w) sorted linked-list insertion from
the revision walk machinery.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28 06:08:20 +09:00
Kristofer Karlsson d877b1af50 revision: introduce rev_walk_mode to clarify get_revision_1()
get_revision_1() dispatches to different walk strategies based on a
combination of rev_info flags: reflog_info, topo_walk_info, and
limited.  These conditions are checked in multiple places within
the function -- once to select the next commit, and again to decide
how to expand parents -- and the two chains must stay in sync.

Extract the mode selection into a rev_walk_mode enum and a small
get_walk_mode() helper, resolved once at the top of get_revision_1().
Both dispatch sites now switch on the same mode variable, making it
obvious that they agree and easier to verify that all modes are
handled.

No functional change.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-28 06:08:20 +09:00
Junio C Hamano 455ff75d35 Merge branch 'ps/setup-wo-the-repository'
Many uses of the_repository has been updated to use a more
appropriate struct repository instance in setup.c codepath.

* ps/setup-wo-the-repository:
  setup: stop using `the_repository` in `init_db()`
  setup: stop using `the_repository` in `create_reference_database()`
  setup: stop using `the_repository` in `initialize_repository_version()`
  setup: stop using `the_repository` in `check_repository_format()`
  setup: stop using `the_repository` in `upgrade_repository_format()`
  setup: stop using `the_repository` in `setup_git_directory()`
  setup: stop using `the_repository` in `setup_git_directory_gently()`
  setup: stop using `the_repository` in `setup_git_env()`
  setup: stop using `the_repository` in `set_git_work_tree()`
  setup: stop using `the_repository` in `setup_work_tree()`
  setup: stop using `the_repository` in `enter_repo()`
  setup: stop using `the_repository` in `verify_non_filename()`
  setup: stop using `the_repository` in `verify_filename()`
  setup: stop using `the_repository` in `path_inside_repo()`
  setup: stop using `the_repository` in `prefix_path()`
  setup: stop using `the_repository` in `is_inside_work_tree()`
  setup: stop using `the_repository` in `is_inside_git_dir()`
  setup: replace use of `the_repository` in static functions
2026-05-27 14:15:46 +09:00
Patrick Steinhardt 920dba4581 setup: stop using `the_repository` in `verify_non_filename()`
Stop using `the_repository` in `verify_non_filename()` and instead
accept the repository as a parameter. The injection of `the_repository`
is thus bumped one level higher, where callers now pass it in
explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-19 19:36:24 +09:00
Patrick Steinhardt 6e7e50cc7b setup: stop using `the_repository` in `verify_filename()`
Stop using `the_repository` in `verify_filename()` and instead accept
the repository as a parameter. The injection of `the_repository` is thus
bumped one level higher, where callers now pass it in explicitly.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-19 19:36:24 +09:00
Kristofer Karlsson ef8d51a8a3 revision: use priority queue in limit_list()
limit_list() maintains a date-sorted work queue of commits using a
linked list with commit_list_insert_by_date() for insertion.  Each
insertion walks the list to find the right position — O(n) per insert.
In repositories with merge-heavy histories, the symmetric difference
can contain thousands of commits, making this O(n) insertion the
dominant cost.

Replace the sorted linked list with a prio_queue (binary heap).  This
gives O(log n) insertion and O(log n) extraction instead of O(n)
insertion and O(1) extraction, which is a net win when the queue is
large.

The still_interesting() and everybody_uninteresting() helpers are
updated to scan the prio_queue's contiguous array instead of walking a
linked list.  process_parents() already accepts both a commit_list and
a prio_queue parameter, so the change in limit_list() simply switches
which one is passed.

Benchmark: git rev-list --left-right --count HEAD~N...HEAD
Repository: 2.3M commits, merge-heavy DAG (monorepo)
Best of 5 runs, times in seconds:

  commits in
  symmetric diff   baseline   patched    speedup
  --------------   --------   -------    -------
            10       0.01      0.01       1.0x
            50       0.01      0.01       1.0x
          3751      21.23      8.49       2.5x
          4524      21.70      8.29       2.6x
         10130      20.10      6.65       3.0x

No change for small traversals; 2.5-3.0x faster when the queue grows
to thousands of commits.

Signed-off-by: Kristofer Karlsson <krka@spotify.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-05-15 04:26:25 +09:00
Junio C Hamano 1678b7de97 Merge branch 'mm/line-log-use-standard-diff-output'
The way the "git log -L<range>:<file>" feature is bolted onto the
log/diff machinery is being reworked a bit to make the feature
compatible with more diff options, like -S/G.

* mm/line-log-use-standard-diff-output:
  doc: note that -L supports patch formatting and pickaxe options
  t4211: add tests for -L with standard diff options
  line-log: route -L output through the standard diff pipeline
  line-log: fix crash when combined with pickaxe options
2026-04-07 14:59:27 -07:00
Pablo Sabater f756a3c78d graph: add --graph-lane-limit option
Replace the hard-coded lane limit with a user-facing
option '--graph-lane-limit=<n>'. It caps the number of
visible lanes to n. This option requires '--graph', without
it, limiting the graph has no meaning, in this case error out.

Zero and negative values are valid inputs but silently
ignored treating them as "no limit", the same as not using
the option. This follows what '--max-parents' does with
negative values.

The default is 0, same as not being used.

Signed-off-by: Pablo Sabater <pabloosabaterr@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-29 21:22:09 -07:00
Jeff King 22b985ef19 revision: avoid writing to const string for parent marks
We take in a "const char *", but may write a NUL into it when parsing
parent marks like "foo^-", since we want to isolate "foo" as a string
for further parsing. This is usually OK, as our "const" strings are
often actually argv strings which are technically writeable, but we'd
segfault with a string literal like:

  handle_revision_arg("HEAD^-", &revs, 0, 0);

Similar to how we handled dotdot in a previous commit, we can avoid this
by making a temporary copy of the left-hand side of the string. The cost
should negligible compared to the rest of the parsing (like actually
parsing commits to create their parent linked-lists).

There is one slightly tricky thing, though. We parse some of the marks
progressively, so that if we see "foo^!" for example, we'll strip that
down to "foo" not just for calling add_parents_only(), but also for the
rest of the function. That makes sense since we eventually want to pass
"foo" to get_oid_with_context(). But it also means that we'll keep
looking for other marks. In particular, "foo^-^!" is valid, though oddly
"foo^!^-" would ignore the "^-". I'm not sure if this is a weird
historical artifact of the implementation, or if there are important
corner cases.

So I've left the behavior unchanged. Each mark we find allocates a
string with the mark stripped, which means we could allocate multiple
times (and carry a free-able pointer for each to the end). But in
practice we won't, because of the three marks, "^@" jumps immediately to
the end without further parsing, and "^-^!" is nonsense that nobody
would pass. So you'd get one allocation in general, and never more than
two.

Another obvious option would be to just copy "arg" up front and be OK
with munging it. But that means we pay the cost even when we find no
marks. We could make a single copy upon finding a mark and then munge,
but that adds extra code to each site (checking whether somebody else
allocated, and if not, adjusting our "mark" pointer to be relative to
the copied string).

I aimed for something that was clear and obvious, if a bit verbose.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-26 12:47:17 -07:00
Jeff King 4d5fb9377b revision: make handle_dotdot() interface less confusing
There are two very subtle bits to the way we parse ".." (and "...")
range operators:

 1. In handle_dotdot_1(), we assume that the incoming arguments "dotdot"
    and "arg" are part of the same string, with the first digit of the
    range-operator blanked to a NUL. Then when we want the full name
    (e.g., to report an error), we replace the NUL with a dot to restore
    the original string.

 2. In handle_dotdot(), we take in a const string, but then we modify it
    by overwriting the range operator with a NUL. This has worked OK in
    practice since we tend to pass in buffers that are actually
    writeable (including argv), but segfaults with something like:

      handle_revision_arg("..HEAD", &revs, 0, 0);

    On top of that, building with recent versions of glibc causes the
    compiler to complain, because it notices when we use strchr() or
    strstr() to launder away constness (basically detecting the
    possibility of the segfault above via the type system).

Instead of munging the buffer, let's instead make a temporary copy of
the left-hand side of the range operator. That avoids any const
violations, and lets us pass around the parsed elements independently:
the left-hand side, the right-hand side, the number of dots (via the
"symmetric" flag), and the original full string for error messages.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-26 12:47:17 -07:00
Michael Montalbo 86e986f166 line-log: route -L output through the standard diff pipeline
`git log -L` has always bypassed the standard diff pipeline.
`dump_diff_hacky()` in line-log.c hand-rolls its own diff headers and
hunk output, which means most diff formatting options are silently
ignored.  A NEEDSWORK comment has acknowledged this since the feature
was introduced:

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

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

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

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

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

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

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-03-16 21:05:42 -07:00
Junio C Hamano d445aecfb0 Merge branch 'ps/refs-for-each'
Code refactoring around refs-for-each-* API functions.

* ps/refs-for-each:
  refs: replace `refs_for_each_fullref_in()`
  refs: replace `refs_for_each_namespaced_ref()`
  refs: replace `refs_for_each_glob_ref()`
  refs: replace `refs_for_each_glob_ref_in()`
  refs: replace `refs_for_each_rawref_in()`
  refs: replace `refs_for_each_rawref()`
  refs: replace `refs_for_each_ref_in()`
  refs: improve verification for-each-ref options
  refs: generalize `refs_for_each_fullref_in_prefixes()`
  refs: generalize `refs_for_each_namespaced_ref()`
  refs: speed up `refs_for_each_glob_ref_in()`
  refs: introduce `refs_for_each_ref_ext`
  refs: rename `each_ref_fn`
  refs: rename `do_for_each_ref_flags`
  refs: move `do_for_each_ref_flags` further up
  refs: move `refs_head_ref_namespaced()`
  refs: remove unused `refs_for_each_include_root_ref()`
2026-03-09 14:36:55 -07:00
Junio C Hamano 7b7d67104e Merge branch 'pw/no-more-NULL-means-current-worktree'
API clean-up for the worktree subsystem.

* pw/no-more-NULL-means-current-worktree:
  path: remove repository argument from worktree_git_path()
  wt-status: avoid passing NULL worktree
2026-03-04 10:53:00 -08:00
Junio C Hamano 9eb5b3b999 Merge branch 'ps/odb-for-each-object'
Revamp object enumeration API around odb.

* ps/odb-for-each-object:
  odb: drop unused `for_each_{loose,packed}_object()` functions
  reachable: convert to use `odb_for_each_object()`
  builtin/pack-objects: use `packfile_store_for_each_object()`
  odb: introduce mtime fields for object info requests
  treewide: drop uses of `for_each_{loose,packed}_object()`
  treewide: enumerate promisor objects via `odb_for_each_object()`
  builtin/fsck: refactor to use `odb_for_each_object()`
  odb: introduce `odb_for_each_object()`
  packfile: introduce function to iterate through objects
  packfile: extract function to iterate through objects of a store
  object-file: introduce function to iterate through objects
  object-file: extract function to read object info from path
  odb: fix flags parameter to be unsigned
  odb: rename `FOR_EACH_OBJECT_*` flags
2026-03-02 17:06:50 -08:00
Junio C Hamano 8d15dd1ce1 Merge branch 'ds/revision-maximal-only'
"git rev-list" and friends learn "--maximal-only" to show only the
commits that are not reachable by other commits.

* ds/revision-maximal-only:
  revision: add --maximal-only option
2026-02-25 11:54:17 -08:00
Patrick Steinhardt 1dd4f1e43f refs: replace `refs_for_each_fullref_in()`
Replace calls to `refs_for_each_fullref_in()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-02-23 13:21:19 -08:00
Patrick Steinhardt 3fc1ad03c6 refs: replace `refs_for_each_glob_ref()`
Replace calls to `refs_for_each_glob_ref()` with the newly introduced
`refs_for_each_ref_ext()` function.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-02-23 13:21:19 -08:00