By admin , 29 April 2026

The reachability bottleneck

Many Git operations need to answer "is commit X reachable from commit Y?" or "which commit is the merge base?" Naively this means walking the commit graph from raw object reads — slow on large repos. The commit-graph file precomputes parent pointers, generation numbers, and (optionally) Bloom filters into a binary side file.

Where it lives

Older Git: .git/objects/info/commit-graph (single file). Newer Git: .git/objects/info/commit-graphs/ (chained, allowing incremental writes).

Writing the graph

git commit-graph write --reachable --changed-paths
git commit-graph verify
git commit-graph write --split=replace --reachable --changed-paths

--reachable includes all ref-reachable commits. --changed-paths adds Bloom filters. --split creates incremental layers.

Auto-write

git config core.commitGraph true
git config gc.writeCommitGraph true
git config fetch.writeCommitGraph true

Enabled by default in Git 2.24+ on most installs. Verify with git config --show-origin core.commitGraph.

Generation numbers

Each commit gets a topological generation number. Comparing them lets Git skip whole graph regions during walks. The "corrected commit date" generation (Git 2.30+) further accelerates queries by combining generation with committer date.

Performance impact

On Linux's repo (over a million commits), git log --oneline | head drops from seconds to milliseconds. git merge-base drops similarly. Tag listing on huge repos becomes near-instant.

Measurement

git -c core.commitGraph=false log -100 --pretty=oneline >/dev/null
time git -c core.commitGraph=false log --oneline | head -10000 >/dev/null
time git log --oneline | head -10000 >/dev/null

Compare the two — usually 5-50x speedup.

Common mistakes

Forgetting to write the graph after a big push or rebase; fetch.writeCommitGraph=true automates it. Disabling core.commitGraph for a "fresh test" without realizing many other features depend on it. Stale graphs after rewriting history; let git maintenance handle this.

Related

See "Changed-path Bloom filters in the commit-graph" and "Background maintenance: git maintenance run/start/stop".