Files
git-mirror/Documentation/gitformat-diff-hunks.adoc
Michael Montalbo 3dc1d97529 diff-hunks: add the store format, library, and command
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>
2026-09-03 07:43:08 -07:00

130 lines
4.8 KiB
Plaintext

gitformat-diff-hunks(5)
=======================
NAME
----
gitformat-diff-hunks - Precomputed diff hunk store format
SYNOPSIS
--------
[verse]
$GIT_DIR/objects/info/diff-hunks
DESCRIPTION
-----------
The diff-hunks store memoizes diff hunk coordinates so that commands
that need them, such as `git log --stat` and linkgit:git-blame[1], can
skip running the diff algorithm (and, for blame, loading the blob
content; the summary formats still test each pair for binariness,
which can load the blobs). See
linkgit:git-diff-hunks[1] for how the store is filled and managed and the
configuration that controls it.
The store is a single file, `$GIT_DIR/objects/info/diff-hunks`, written
in one pass and replaced atomically, so a reader sees either the old
file or the complete new one.
Entries are keyed by the object IDs of the blob pair that was diffed
and by the diff algorithm and ignore flags (`xdl_opts`) the pair was
diffed under. A blob pair fully determines the diff input, so an entry
is valid regardless of which commits, branches, or index states the
pair was encountered in, and identical diffs performed in different
contexts share one entry. A reader whose `xdl_opts` differ from an
entry does not match it and falls back to computing the diff.
FILE FORMAT
-----------
All multi-byte integers are stored in network byte order. The file is an
8-byte header, the chunk table of contents and chunk data described in
linkgit:gitformat-chunk[5], and a trailing checksum.
HEADER
~~~~~~
- 4-byte signature: `DHPF` (diff-hunks precomputed format)
- 1-byte version number: currently 1
- 1-byte hash version: 1 for SHA-1, 2 for SHA-256. A store whose hash
function differs from the repository's is ignored.
- 1-byte number of chunks
- 1-byte reserved
CHUNK LOOKUP
~~~~~~~~~~~~
A table of contents in the format of linkgit:gitformat-chunk[5], listing
the offset of each chunk. Both chunks below are required; a file missing
either is treated as corrupt.
CHUNK DATA
~~~~~~~~~~
DHIX (index)::
A sorted sequence of fixed-size entries. Each entry is the old
blob object ID, the new blob object ID, a 4-byte `xdl_opts`
value, and a 4-byte offset into the DHDT chunk. Entries are
sorted by old object ID, then new object ID, then `xdl_opts`,
so lookups can use binary search on the full key.
DHDT (hunk data)::
For each index entry, at its offset: a 4-byte hunk count followed
by that many 16-byte hunk records. A hunk record is four 4-byte
values: old start, old count, new start, new count.
Starts are 0-based line numbers in the old and new blob; counts
are numbers of lines. The hunk count is at least 1: a record with
no hunks would claim the blob pair equivalent, which the store
never records, so readers treat such a record as invalid.
Identical hunk blocks are stored once:
distinct index entries whose recorded hunks are byte-for-byte
equal point at the same offset.
TRAILER
~~~~~~~
A checksum of all preceding bytes, computed with the repository hash
function.
CORRECTNESS
-----------
Serving hunks from a valid store produces the same output as recomputing
the diff. The diff of a blob pair is not unique: a zero context length
triggers xdiff's common-tail trimming, which can pick a different but
equally valid set of hunks than an untrimmed diff does. A pair is
therefore recorded only when its trimmed and untrimmed diffs are
identical, which is the common case. Such an entry answers any consumer
at any context: git-blame replays its coordinates directly (it diffs at
zero context), and diffstat sums its per-hunk line counts, which the
context length does not change. The rare pair whose two diffs differ is
never recorded, so every consumer computes it.
A store that cannot be used is ignored, and the consumer falls back to
computing the diff. Every offset and count read from the file is
bounds-checked, so a store that is missing, truncated, of an unknown
version, or of a different object hash does not change the diff output
and does not produce a diagnostic; `git diff-hunks verify` is what
reports corruption.
The store is not re-checksummed on the read path. The writer fsyncs the
file (honoring `core.fsync`) and commits it atomically, so a
committed store is intact, the same trust model the commit-graph and
multi-pack-index use. The trailing checksum is recomputed by
`git diff-hunks verify` to detect corruption.
The checksum detects corruption but does not prove who wrote the file. A
reader trusts the coordinates in a store that passes its checks, so
anything able to write a checksum-valid file at the store path can
influence output, the same as it could by writing objects directly.
LIMITATIONS
-----------
- Hunk counts, offsets, and line coordinates are 32-bit, capping the
hunk data at 4 GiB and a single entry at roughly 268 million hunks.
A result whose coordinates cannot be represented is not recorded.
GIT
---
Part of the linkgit:git[1] suite