Enabling the commit-graph and multi-pack-index Jump to heading

Two of Git’s slowest operations in a large repository are walking history and finding objects. Walking history β€” for git log --graph, git merge-base, ahead/behind counts, git branch --contains β€” normally means parsing commit objects one at a time from compressed packs. Finding an object means checking the index of every pack file in turn, and a busy repository can accumulate dozens of packs between repacks. The commit-graph file stores commit parents, root trees and generation numbers in a compact, directly indexable form, so history walks can skip most parsing and stop early. The multi-pack-index (MIDX) is a single index across all pack files, so lookups check one index instead of many. Both are safe, additive files Git can always rebuild. This page enables them, verifies they are used, and keeps them current, within large repository performance.

When to use this approach Jump to heading

  • git log --graph, git status ahead/behind counts or git merge-base are slow.
  • The repository has hundreds of thousands of commits or many pack files.
  • Server-side operations such as reachability checks during push take noticeably long.
  • You already schedule maintenance, as in scheduling git maintenance for background repacking.

Step 1 β€” Measure the operations you want to speed up Jump to heading

Time representative commands before changing anything, so you can tell whether the files help and by how much.

git count-objects -v | grep -E 'count|packs|size-pack'
ls .git/objects/pack/*.pack | wc -l
time git log --oneline --graph -n 2000 >/dev/null
time git merge-base origin/main HEAD
time git rev-list --count origin/main
time git branch -r --contains HEAD~500 >/dev/null
What each file acceleratesThe commit-graph stores commit parents, trees, dates and generation numbers, so history walks for log, merge-base, contains and ahead-behind counts avoid parsing commits and can stop early. The multi-pack-index puts every pack's object index into one file, so finding an object checks a single index.commit-graphparents, trees, gen numberslog --graphmerge-basebranch --containsmulti-pack-indexone index over all packsobject lookupfetch / push negotiationmany packs β†’ one searchboth are derived data: deleting them never loses anything

Step 2 β€” Write the commit-graph Jump to heading

Write a commit-graph covering all reachable commits, and enable reading it. Recent Git versions read it by default; the config makes the intent explicit and enables writing it during fetch.

git config core.commitGraph true
git config fetch.writeCommitGraph true          # update incrementally after each fetch
git commit-graph write --reachable --changed-paths
git commit-graph verify
ls -la .git/objects/info/commit-graph* .git/objects/info/commit-graphs 2>/dev/null

--changed-paths adds Bloom filters recording which paths each commit changed, which speeds up git log -- <path> and git blame considerably in large trees.

Step 3 β€” Write the multi-pack-index Jump to heading

Enable the multi-pack-index and write it over the current packs.

git config core.multiPackIndex true
git multi-pack-index write --bitmap        # bitmap speeds up reachability for clones and fetches served from here
git multi-pack-index verify
ls -la .git/objects/pack/multi-pack-index*

The bitmap option matters most on servers and mirrors that serve clones; on a developer machine the index alone is the main win.

Step 4 β€” Re-measure Jump to heading

Repeat the timings from Step 1. The largest improvements usually appear in history walks over long ranges and path-limited log.

time git log --oneline --graph -n 2000 >/dev/null
time git log --oneline -- services/billing/ >/dev/null
time git branch -r --contains HEAD~500 >/dev/null
Time for common commands before and afterIllustrative timings from a repository with around a million commits. Path-limited log and branch contains checks improve the most, because the commit-graph and changed-path filters let Git skip commits that cannot match. Simple status is unaffected.seconds, before β†’ after (illustrative)log -- path (before)41 slog -- path (after)4 sbranch --contains (before)18 sbranch --contains (after)1 smerge-base (before)3 smerge-base (after)0.2 smeasure your own repository β€” gains depend on history shape

Step 5 β€” Keep the files current Jump to heading

Both files go stale as new commits and packs arrive; Git still works, but new data is not covered. git maintenance updates them incrementally on a schedule.

git maintenance start                            # registers the repo, enables scheduled tasks
git config maintenance.commit-graph.enabled true
git config maintenance.incremental-repack.enabled true    # also rewrites the multi-pack-index
git maintenance run --task=commit-graph --task=incremental-repack

On servers, run the same tasks from the hosting platform’s maintenance jobs or a cron entry, and make sure the server’s Git version supports the bitmap format you write.

Step 6 β€” Roll it out to every clone Jump to heading

These files are local to each clone; they are not pushed or fetched. Make them part of developer setup and CI runner images.

# In the team bootstrap script
git config --global fetch.writeCommitGraph true
git config --global core.commitGraph true
git config --global core.multiPackIndex true
for r in "$HOME"/src/*/; do git -C "$r" maintenance register 2>/dev/null; done

Machine setup is covered in bootstrapping a developer machine for Git. CI runners that reuse a mirror benefit too, as in reusing a Git mirror on self-hosted runners.

Step 7 β€” Recover from a corrupt or mismatched file Jump to heading

Because both files are derived, the fix for any problem is to delete and rewrite them. Symptoms are errors mentioning commit-graph or multi-pack-index, often after copying a repository between machines with different Git versions.

git commit-graph verify || { rm -rf .git/objects/info/commit-graph .git/objects/info/commit-graphs; git commit-graph write --reachable --changed-paths; }
git multi-pack-index verify || { rm -f .git/objects/pack/multi-pack-index*; git multi-pack-index write; }
Git reports an error about these files β€” what now?If verify fails, delete the file and write it again, since it holds only derived data. If an older Git version cannot read a newer file format, upgrade Git or rewrite the file with the older version. If errors persist after rewriting, run a full fsck, because the problem is in the objects rather than the index files.What does the error suggest?verify failsDelete + rewritederived data onlyunknown versionVersion mismatchupgrade Gitpersists after rewriteRun git fsckobjects damagedremoving these files is always safe β€” Git just runs slower until they are rewritten

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Does this change the repository content or history? Jump to heading

No. Both files are indexes derived from the objects. Deleting them returns the repository to exactly its previous state, only slower.

Is the commit-graph used by default now? Jump to heading

Reading it is on by default in current Git versions, and git gc writes it. Enabling fetch.writeCommitGraph and maintenance keeps it current between garbage collections, which is where most clones fall behind.

Do hosted forges do this for us? Jump to heading

Forges maintain their own server-side copies. Your local clones still need their own files, because none of this is transferred by fetch or clone.