Maintain Git's Multi-Pack Index Without Guessing

git multi-pack-index ties several packfiles together into one lookup, so Git stops hunting pack by pack. A repository that has accumulated packfile after packfile from years of pushes gets slower to query until something does exactly that. You will finish with a verified multi-pack-index (MIDX) for a repository, plus a safe way to decide whether expiry or repacking is actually appropriate. The examples match Git 2.43.0 from Ubuntu package git 1:2.43.0-1ubuntu7.3 and its matching git-man package.

1. Check Git and the repository

Start from the repository whose object database you want to maintain. Replace the placeholder with an absolute path, or use . when you are already in the repository:

$ cd /path/to/repository
$ git --version
git version 2.43.0
$ git rev-parse --show-toplevel
/path/to/repository
$ git count-objects -v

That count is a useful baseline. In particular, packs tells you how many packfiles Git currently sees. A MIDX can be written even with only one pack, but it earns its keep once several packs accumulate between maintenance runs.

Checkpoint: if git rev-parse fails, stop and correct the path. Do not run maintenance commands from a directory that merely happens to share a project's name.

2. Write the multi-pack-index

Write the index for the current repository with:

$ git multi-pack-index write

Git writes the MIDX below the repository's object store, normally .git/objects/pack/multi-pack-index. Its format records pack names, object IDs, and the pack and offset selected for each object; a duplicate object is represented once in the index, not once per pack.

Progress only shows when standard error is connected to a terminal, so a command running from a scheduler or with standard error redirected can look silent even while it works. That is a display default, not evidence the command did nothing. Ask for a definite choice with --progress or --no-progress instead:

$ git multi-pack-index --progress write

Checkpoint: verify the file itself rather than trusting its timestamp or a progress message.

3. Verify the index

Run the read-only check immediately after writing:

$ git multi-pack-index verify
$ printf '%s\n' $?
0

A zero status means the MIDX contents passed Git's verification. The command checks the index against the pack data it references; it does not repair a bad pack, fetch missing objects or repack anything. If verification reports an error, preserve the output and investigate the named pack before deleting a thing.

Run the same check again after copying a repository, restoring a backup, or completing any other storage operation. verify takes no pack-selection argument: it checks the current MIDX for the current object directory, nothing else.

4. Choose a preferred pack only when you have a reason

When the same object occurs in more than one pack, Git normally breaks the tie in favour of the pack with the lowest modification time. Override that tie-breaker by naming a pack with --preferred-pack; the value must identify a pack containing at least one object:

$ find .git/objects/pack -maxdepth 1 -name 'pack-*.pack' -printf '%f\n'
pack-EXAMPLE.pack
$ git multi-pack-index write --preferred-pack=pack-EXAMPLE.pack

Use the pack basename your Git installation actually expects, then verify again. Choosing a preferred pack is a policy decision for later bitmap or pack-reuse behaviour, not a way to make a damaged pack trustworthy. With no operational reason to prefer one pack, leave the option out and keep Git's documented mtime rule.

5. Add a bitmap only as part of a measured plan

A multi-pack bitmap can speed up object reachability operations, but writing one adds work and another index artefact to account for, so request it explicitly:

$ git multi-pack-index write --bitmap
$ git multi-pack-index verify

If a repacking process already took a reference snapshot, pass that readable file with --refs-snapshot=/path/to/refs-snapshot alongside --bitmap. The snapshot holds one object ID per line, with an optional leading + marking a preferred reference tip, and it may contain duplicate IDs. Do not invent a snapshot from arbitrary text: it is input to bitmap generation, not a general repository log. Use --no-bitmap to drop bitmap generation from a write command; that changes the index output and can affect later performance, so note the reason in your maintenance records.

6. Handle an alternate object directory

For an alternate object store, point Git at the directory containing packs/:

$ git multi-pack-index --object-dir=/path/to/alternate-objects write
$ git multi-pack-index --object-dir=/path/to/alternate-objects verify

Git looks for the MIDX at /path/to/alternate-objects/pack/multi-pack-index and for packfiles in /path/to/alternate-objects/pack. The directory must already be a configured alternate of the current repository; if it is not, fix the repository's alternate-object configuration rather than writing an index against an unrelated object store.

Checkpoint: list the directory you intend to affect before running the command:

$ find /path/to/alternate-objects/pack -maxdepth 1 -type f -printf '%f\n' | sort

7. Understand repack before you change storage

Warning: repack creates a new pack and changes which packfiles the MIDX references. The old, smaller packs become candidates for a later expiry, so take a backup, or confirm your normal repository backup is current, before running it on valuable data.

With a non-zero batch size, Git examines packs from oldest to newest and estimates each pack's useful size from the objects the MIDX selects. It keeps selecting packs until the requested batch size is reached or every pack has been considered; if only one pack gets selected, it does nothing. A zero batch size means every object the MIDX references goes into the new pack:

$ git multi-pack-index repack --batch-size=1g
$ git multi-pack-index verify

Do not assume 1g suits every repository; set it from measured disk and maintenance constraints. Packs carrying a .keep file are skipped when repack.packKeptObjects is false, so check that setting before expecting a kept pack to move.

8. Expire old packs only after verification

Warning: expire deletes packfiles tracked by the MIDX that no longer contain any object the MIDX references, then rewrites the MIDX itself. It skips packs protected by a .keep file or cruft packs, but it is still a destructive storage operation. Confirm nothing is writing to the repository, and that you have a recoverable backup, before running it:

$ git multi-pack-index verify
$ git multi-pack-index expire
$ git multi-pack-index verify
$ git count-objects -v

Recovery: if the first verification fails, do not continue to expiry. If the final verification fails, stop further maintenance and restore the affected repository from backup, or investigate the exact pack Git names. There is no undo switch for a packfile expiry removes; recovery depends entirely on your backup or another complete copy of the object database.

Common traps

Done means