Home / Alt manpages / gitformat-commit-graph(5)

  • gitformat-commit-graph(5)
  • File format
  • linux

Build and Verify Git Commit-Graphs Safely

A slow git log on a big history is usually a missing commit-graph. This guide gets you writing one, adding changed-path Bloom filters where they help, verifying the result against the object database, and knowing exactly where Git keeps it. The examples use Git 2.43.0, the version installed with the local manpages.

Allow roughly five minutes for a small repository. A large one can take much longer once Git starts computing changed paths, so schedule that step away from busy hours if it is competing for disk or CPU.

Before you start

  1. Pick a repository where you can run Git commands as its owner.
  2. Check the version and state before touching anything.
cd /path/to/repository
git --version
git rev-parse --show-toplevel
git status --short

Expected version output for the machine described in this guide:

git version 2.43.0

A commit-graph is derived data living under the repository's object directory. It does not touch commits, branches, the index, or working-tree files. The write command can still eat real resources, so the files it creates belong in any repository backup plan.

Checkpoint: write the graph

Start with every commit reachable from the repository's refs. The --changed-paths option also writes Bloom-filter data for paths changed between each commit and its first parent, which can make commands like git log -- path/to/file faster at the cost of more work up front:

git commit-graph write --reachable --changed-paths --progress

On a terminal, progress prints to standard error. A small repository normally ends with something like:

Writing out commit graph: 100% (1/1), done.

The exact percentage and wording depend on the repository. If the command reports that core.commitGraph is disabled, it may return success without writing a thing, so check that setting before mistaking a no-op for a successful build:

git config --show-origin --get core.commitGraph || echo 'core.commitGraph is unset'

When it is explicitly false, enable it only for this repository:

git config --local core.commitGraph true

That writes .git/config, not anything system-wide. Undo it later with:

git config --local --unset core.commitGraph

After unsetting it, Git's built-in default applies again. Skip --global unless you deliberately want to affect other repositories too.

Checkpoint: find the generated file

For a normal repository, the single graph lands at .git/objects/info/commit-graph. A split graph instead uses a chain under .git/objects/info/commit-graphs/. Ask Git for the object directory rather than guessing, a linked worktree or alternate object store can point somewhere else entirely:

object_dir=$(git rev-parse --git-path objects)
printf '%s\n' "$object_dir"
find "$object_dir/info" -maxdepth 2 -type f -name 'commit-graph*' -printf '%P %s bytes\n' 2>/dev/null

The first command's typical output is an absolute path ending in /.git/objects. The graph itself is binary: do not open it in a text editor, and do not trust the output of file alone as proof it is valid.

Checkpoint: verify against the object database

Run Git's verifier right after writing, and again after restoring a repository from backup:

git commit-graph verify --progress

A successful run looks like this:

Verifying commits in commit graph: 100% (1/1), done.

The count reflects your repository, so yours will differ. Verification actually reads the graph and checks it against the object database, that is the integrity check that matters; a file merely existing tells you nothing.

If the repository has a split chain and you want a quick tip-layer check, --shallow looks only at the tip commit-graph. Use the full command above when the whole chain needs checking:

git commit-graph verify --shallow --progress

Use split graphs for incremental maintenance

A split graph stores layers below objects/info/commit-graphs, handy when you update a repository often and want new commits added as a tip layer instead of a full rewrite. This form never merges layers:

git commit-graph write --reachable --split=no-merge --changed-paths --progress

Git's default split strategy can merge layers according to its own size rules. --split=replace deliberately replaces the whole chain with a fresh graph covering the selected commits. Treat that as a maintenance operation, it replaces derived files, not repository history.

To rebuild every reachable commit and recompute older Bloom filters:

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

Do not run this repeatedly on a large repository without a reason, changed-path computation takes real time. If you want to cap new filters, --max-new-filters=10000 limits filters generated for the new layer, and the manpage documents -1 as unlimited. A cap trades some path-history speed for shorter maintenance runs.

Adjust reading behaviour

Git 2.43.0 defaults commitGraph.readChangedPaths to true. Setting it false makes Git ignore changed-path Bloom filters even when they exist on disk:

git config --local commitGraph.readChangedPaths false
git config --local --get commitGraph.readChangedPaths

Expected output:

false

This only changes reading behaviour, not the graph file itself. Restore the default by removing the local override:

git config --local --unset commitGraph.readChangedPaths

The other version-specific setting is commitGraph.generationVersion. The local Git documentation gives 2 as the default, which allows corrected commit dates in generation data; version 1 omits that data on write or read. Leave the default alone unless you have a tested compatibility reason to change it:

git config --local commitGraph.generationVersion 2
git config --local --get commitGraph.generationVersion

Configuration changes here are ordinary repository-local writes and never need root. If another process owns or restricts the repository, fix that filesystem permission problem directly rather than running the maintenance command as root, which can leave root-owned graph or configuration files behind for you to clean up later.

Common failure traps

  • Nothing appears after write: check core.commitGraph. False disables writing while still allowing a successful return.
  • Bloom filters are missing: the graph may have been written without --changed-paths, or an older layer may not carry them. Rebuild with --split=replace --changed-paths when the extra work is worth it.
  • Verification fails: stop trusting the graph until the object database and graph agree again. Keep the diagnostic, check for an incomplete restore or damaged objects, and only rebuild once the objects themselves are confirmed intact.
  • A command feels slow: a full reachable walk plus changed-path calculation is a normal cost on a large history. Use a maintenance window and keep an eye on disk space.
  • You delete the wrong thing: never manually remove files while Git is mid-write. To discard derived data on purpose, stop any concurrent Git maintenance first, record the current file list, and prefer Git's own split-writing options to manage the chain.

Done means

  • The write completed: git commit-graph write --reachable --changed-paths --progress finished without an error or an unexplained no-op.
  • The file exists: a graph sits under the object directory reported by git rev-parse --git-path objects.
  • Verification passed: git commit-graph verify --progress completed successfully.
  • Overrides are deliberate: any core.commitGraph or commitGraph.* override is intentional and reversible with git config --local --unset.