mirror of
https://github.com/git/git.git
synced 2026-10-05 22:35:45 +02:00
Blame and "git log --stat" recover hunk coordinates by diffing blob pairs, and recompute them on every run. Add a cache of those coordinates at $GIT_DIR/objects/info/diff-hunks, beside the commit-graph, so a later run can look them up instead of decompressing the blobs and running xdiff again. The store is a single chunk-format file (see gitformat-chunk(5)): an 8-byte header, a DHIX index of fixed-size entries sorted by key, a DHDT segment of hunk records, and a trailing hash checksum. An entry is keyed by the two blob object ids and the xdl_opts the pair was diffed under, so a stored result is served only where that exact key recurs, independent of path. A zero-context diff trims unchanged lines from hunk edges and can pick a different but equally valid set of hunks than an untrimmed diff, so a recording caller stores a pair only when its trimmed and untrimmed diffs are identical; such an entry answers any consumer at any context, and the rare divergent pair is always computed. Identical hunk blocks are interned once and shared across keys. The library provides a reader (repo_diff_hunks_store and _replay, gated by core.diffHunks), loaded once and cached on the object database as the commit-graph is, and a writer that accumulates entries and flushes them in one atomic pass. An absent, corrupt, or disabled store reads as all misses. A record with no hunks is invalid too: replaying it would claim the pair equivalent, which the store never asserts, so it reads as a miss. Ordinary reads are diagnostic-free. Loading parses the chunk table through read_table_of_contents_quiet(), new in chunk-format, which prints nothing on a malformed table and takes the repository's hash algorithm rather than the_hash_algo, so the file is bounds-checked under the algorithm it is keyed by. The flush closes the repository's mmapped store and forgets that loading was attempted before committing the lockfile. A warming run that also reads may hold the file it is replacing mapped, and the rename must not land on a live mapping, which Windows refuses; a read after the flush then observes the committed file. commit-graph closes its graph before committing for the same reason. Writing is off by default, enabled per run by GIT_DIFF_HUNKS_WRITE or persistently by diffHunks.write, the environment winning. A writer seeds from the existing store, so a flush merges rather than replaces. The seed's checksum is verified first: a corrupt store is discarded, not rewritten with a fresh checksum verify could no longer catch. An entry that fails the shared diff_provider_check_hunk() or names no blob is dropped with a warning, since it would only ever read as a miss. A seed that discarded or dropped anything forces the flush even when the warming run computed nothing new. The writer fsyncs through a new diff-hunks core.fsync component. "git diff-hunks" inspects and manages the file: "verify" checks the checksum, chunk table, sort order, entry bounds, and every entry's hunk sequence against that shared check, so a store whose entries could only read as misses fails verify; "clear" removes the file. Later patches wire the readers and the writer into the diff and blame paths. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
147 lines
5.6 KiB
Plaintext
147 lines
5.6 KiB
Plaintext
git-diff-hunks(1)
|
|
=================
|
|
|
|
NAME
|
|
----
|
|
git-diff-hunks - Inspect and manage the diff-hunks store
|
|
|
|
SYNOPSIS
|
|
--------
|
|
[synopsis]
|
|
git diff-hunks verify
|
|
git diff-hunks clear
|
|
|
|
DESCRIPTION
|
|
-----------
|
|
|
|
The diff-hunks store is a cache of diff hunk coordinates, the line
|
|
ranges that changed between two blobs, so that commands
|
|
which need them, such as linkgit:git-blame[1] and `git log` and `git diff`
|
|
with the `--stat`, `--numstat`, and `--shortstat` formats, can skip
|
|
running the diff algorithm, and blame can skip loading the blob
|
|
content. (The summary formats still test each pair for binariness,
|
|
which can load the blobs.)
|
|
|
|
The store is a single file, `$GIT_DIR/objects/info/diff-hunks`. Reading is
|
|
enabled by default; writing is off by default. A `git diff`, `git log`,
|
|
`git show`, or `git diff-tree` that produces one of the stat formats
|
|
fills the store as a side effect, but only when writing is enabled for
|
|
that run (see "WARMING THE STORE" below), so ordinary reads never
|
|
modify the repository. When the store does not have the pair, holds a
|
|
different object hash, the file is unreadable, or an object replacement
|
|
redirects one of the blobs, the consumer falls back to computing the
|
|
diff. A store only speeds up these commands; it never changes their
|
|
output.
|
|
|
|
`git diff-hunks` itself only inspects and manages the file. See
|
|
linkgit:gitformat-diff-hunks[5] for the file format.
|
|
|
|
WARMING THE STORE
|
|
-----------------
|
|
|
|
The store is filled by running ordinary commands with writing enabled.
|
|
Turn writing on for a single invocation with the `GIT_DIFF_HUNKS_WRITE`
|
|
environment variable, or persistently with the `diffHunks.write`
|
|
configuration; the environment variable takes precedence. A repository
|
|
owner warms the store by running the diff-producing commands they care
|
|
about with writing on, for example:
|
|
|
|
GIT_DIFF_HUNKS_WRITE=1 git log --all --stat >/dev/null
|
|
|
|
A `--stat` walk records one entry per blob pair;
|
|
linkgit:git-blame[1] replays the coordinates and the summary formats
|
|
sum the counts, so a single warming walk serves both.
|
|
A warming run seeds from the existing store and rewrites the file
|
|
with the newly computed pairs merged in, so a later run adds to what
|
|
earlier runs recorded rather than discarding it.
|
|
|
|
A walk records only the pairs it diffs. `git log --all --stat` diffs
|
|
each commit against its first parent, so a blame that follows a
|
|
merge's second parent computes those pairs itself: blame coverage is
|
|
partial on history with merges. Warming with a walk that also diffs
|
|
the other parents, for example `git log --all -m --stat`, raises
|
|
blame coverage at the cost of a larger store and a longer warming
|
|
run.
|
|
|
|
COMMANDS
|
|
--------
|
|
|
|
`verify`::
|
|
Check the integrity of the store: the trailing hash checksum, the
|
|
chunk table of contents, the sort order of the index, and the
|
|
bounds of every entry. Exits with non-zero status if the store is
|
|
corrupt. An absent store is valid.
|
|
|
|
`clear`::
|
|
Remove the store file.
|
|
|
|
CORRECTNESS
|
|
-----------
|
|
|
|
A stored result is interchangeable with a freshly computed one because an
|
|
entry is keyed by the inputs that determine the diff:
|
|
|
|
* the object IDs of the old and new blob, so a result is used only for
|
|
the exact contents it was computed from; and
|
|
* the diff algorithm and ignore flags (`xdl_opts`) the hunks were
|
|
computed under. A lookup whose `xdl_opts` differ from a stored entry
|
|
misses. This is why, for example, `blame -w` and
|
|
`--diff-algorithm=<algorithm>` (including a per-path
|
|
`diff.<driver>.algorithm`) do not reuse entries recorded under the
|
|
default settings: they change `xdl_opts`.
|
|
|
|
The context length is not part of the key because only trim-stable
|
|
pairs are recorded: pairs whose zero-context trimmed diff and untrimmed
|
|
diff are identical, so one entry answers blame (zero context) and the
|
|
summary formats (any context) alike. The rare pair where
|
|
the zero-context trimming optimization picks a different but
|
|
equally valid set of hunks is
|
|
never recorded and is always computed.
|
|
|
|
Some options shape the hunks in ways the key does not express, so a
|
|
diff that uses them is excluded from the store in both directions:
|
|
break detection (`-B`), `--ignore-matching-lines` (`-I`), and
|
|
`--anchored`. `--ignore-blank-lines` is different: it is an ignore
|
|
flag and therefore part of the key, but the summary formats exclude
|
|
it anyway, because it coalesces hunks differently between the code
|
|
path that emits text and the one that replays coordinates, so a
|
|
served answer would not match a store-less run.
|
|
linkgit:git-blame[1] additionally does not
|
|
consult the store for reverse blame, ignored revisions, or paths with a
|
|
textconv driver.
|
|
|
|
The store carries a trailing hash checksum, but readers do not
|
|
re-checksum it on every load. As with the commit-graph and
|
|
multi-pack-index, the writer fsyncs the file (honoring `core.fsync`) and
|
|
commits it atomically, so a committed store is intact; every offset and
|
|
count is still bounds-checked as it is read. The checksum is verified by
|
|
`git diff-hunks verify`, not on the read path, so structural corruption
|
|
that fails a bounds check is read as an absent entry, while a record
|
|
that stays within bounds but whose bytes were altered is served until
|
|
`verify` detects the mismatch.
|
|
|
|
CONFIGURATION
|
|
-------------
|
|
|
|
`core.diffHunks`::
|
|
Whether commands read the store. Defaults to true. See
|
|
linkgit:git-config[1].
|
|
|
|
`diffHunks.write`::
|
|
Whether diff-producing commands write to the store. Defaults to
|
|
false. The `GIT_DIFF_HUNKS_WRITE` environment variable overrides it
|
|
for a single invocation. See linkgit:git-config[1].
|
|
|
|
Writing the store honors the `core.fsync` configuration through the
|
|
`diff-hunks` component; see linkgit:git-config[1].
|
|
|
|
SEE ALSO
|
|
--------
|
|
linkgit:git-blame[1],
|
|
linkgit:git-log[1],
|
|
linkgit:gitformat-diff-hunks[5]
|
|
|
|
GIT
|
|
---
|
|
Part of the linkgit:git[1] suite
|