When Your Repository Becomes a Bottleneck
I first noticed this problem about 8 months ago, when our team migrated a large monorepo — over 15 years of commit history, nearly 2GB of pack files — to a new server. git status was taking 3–4 seconds, and git log --oneline -10 had noticeable lag. At first I thought it was a hardware issue, but after profiling, the root cause was somewhere else entirely: Git had to scan through too many individual pack files every time it looked up an object.
Git stores objects (commits, trees, blobs) in pack files — compressed binary files inside .git/objects/pack/. When a repository has just been cloned or recently had git gc run on it, there are usually only 1–2 pack files and everything stays fast. But after months of activity — continuous fetches, CI/CD running 24/7 — the number of pack files can climb into the dozens. Each pack file has its own index (.idx), and Git has to binary-search through each one to find an object. The more files there are, the greater the overhead.
The traditional solution is git gc --aggressive to consolidate everything into a single pack. But on a 2GB repo, that command runs for 20–30 minutes and blocks all operations during that time. Not viable for production.
How Git Multi-Pack-Index Solves the Problem
Multi-Pack-Index (MIDX) — introduced in Git 2.21 and stable since Git 2.25 — creates a single shared index file covering all existing pack files, without merging them together. This file lives at .git/objects/pack/multi-pack-index.
Instead of sequentially binary-searching through N .idx files, Git only needs a single lookup in the MIDX — which immediately tells it which pack file contains the object and at what offset. With 30 pack files, this is a significant improvement that requires no changes to the underlying data.
The most important point: MIDX does not modify existing pack files, does not risk data loss, and requires no downtime. Once created, it’s immediately in effect.
Enabling MIDX
Git 2.25+ ships with MIDX built in — nothing extra to install. Check your version first:
git --version
# git version 2.43.0 — OK, MIDX is supported
Enable MIDX for your repository:
# Enable globally (recommended for all repos)
git config --global core.multiPackIndex true
# Or only for the current repo
git config core.multiPackIndex true
Create the MIDX file for the first time:
cd /path/to/your/repo
git multi-pack-index write
# Confirm the file was created
ls -lh .git/objects/pack/multi-pack-index
On a large repo, the initial MIDX creation may take 10–30 seconds — but it doesn’t block anything; Git continues serving other operations normally in the meantime.
Detailed Configuration and Workflow Integration
Automatically Updating MIDX with git maintenance
MIDX is created once but doesn’t update itself. Every time a new pack file appears — after a fetch or gc — the existing MIDX no longer covers everything. You need to re-run git multi-pack-index write periodically.
Git 2.29+ includes git maintenance, which handles exactly this:
# Enable maintenance for the current repo
git maintenance start
# Check with a dry-run first
git maintenance run --task=incremental-repack --dry-run
git maintenance start registers a cron job (Linux/macOS) or Task Scheduler entry (Windows) to run maintenance periodically. The incremental-repack task will automatically update MIDX after each fetch without any manual intervention.
If you prefer more manual control, add this to your post-merge hook:
# .git/hooks/post-merge (run chmod +x after creating)
#!/bin/bash
git multi-pack-index write 2>/dev/null &
Running it in the background (&) avoids slowing down the developer’s merge/fetch operations.
Combining with commit-graph
After enabling MIDX, I also enabled commit-graph — the two features complement each other well. MIDX optimizes object lookups, while commit-graph speeds up reachability queries like git log --graph and git merge-base:
git config --global fetch.writeCommitGraph true
git config --global core.multiPackIndex true
# Create both at the same time
git multi-pack-index write --bitmap
git commit-graph write --reachable --changed-paths
The --bitmap flag when writing MIDX generates an additional bitmap index — useful for server-side bare repositories. For local dev repos it’s not particularly necessary.
Configuration for Bare Repositories on a Server
If you self-host Gitea or GitLab, server-side configuration matters far more than local settings:
# On a bare repo (server)
git config pack.writeBitmaps true
git config repack.writeBitmaps true
git config core.multiPackIndex true
# Run maintenance — does not block client push/fetch
git maintenance run \
--task=gc \
--task=commit-graph \
--task=incremental-repack
Schedule this to run at 2 AM to avoid peak hours:
# crontab -e
0 2 * * * cd /path/to/bare.git && git maintenance run --task=incremental-repack --task=commit-graph 2>>/var/log/git-maintenance.log
Verification and Monitoring
Verifying Integrity
# Verify MIDX has no errors (silent if OK, prints errors if problems found)
git multi-pack-index verify
# View detailed MIDX statistics
git multi-pack-index verify --object-dir=.git/objects
# Debug whether Git is loading the MIDX
GIT_TRACE2=1 git status 2>&1 | grep -i multi_pack
Measuring Real-World Improvement
Before enabling MIDX, I benchmarked with a simple approach:
# Drop the OS page cache before each measurement (Linux)
sudo bash -c 'echo 3 > /proc/sys/vm/drop_caches'
# Measure time WITHOUT MIDX
time git rev-list --count HEAD
time git log --oneline -50 > /dev/null
# Create MIDX
git multi-pack-index write
# Drop cache and measure again
sudo bash -c 'echo 3 > /proc/sys/vm/drop_caches'
time git rev-list --count HEAD
time git log --oneline -50 > /dev/null
Results on our team’s repo (28 pack files, ~1.8GB): git rev-list --count HEAD dropped from 4.2s to 0.8s. git log --oneline -50 went from 2.1s down to 0.4s. With a team of 8, I rolled out this Git configuration across all developer machines and the CI/CD server — complaints about “slow git” dropped noticeably, especially after sprints with heavy branch merging.
When to Rebuild MIDX
A few situations that call for re-running git multi-pack-index write:
- After any
git fetchthat creates a new pack file (if you’re not using the maintenance hook) - After
git gcorgit repack— these commands change the pack files - After a large rebase or merging a long-lived branch with many commits
- When the number of loose objects spikes unexpectedly
# Check how many pack files currently exist
ls .git/objects/pack/*.pack | wc -l
# If > 10, it's worth rewriting MIDX
git multi-pack-index write
If the pack file count exceeds 50–100, consider running git repack -adf once to consolidate them — MIDX helps defer that point, but it won’t hold it off forever.
Practical Notes After 6 Months in Production
- Git 2.25 is sufficient — Ubuntu 20.04 LTS ships with Git 2.25.1, which is enough for basic MIDX usage. For
git maintenanceyou’ll need Git 2.29+. - MIDX only optimizes read operations —
git add,git commit, andgit pushwon’t be faster. The clearest gains are with:git log,git status,git fetch, andgit merge. - The MIDX file is surprisingly small — a repo with 1.8GB of pack files produces a
multi-pack-indexfile of only ~2MB. Negligible disk impact. - Safe to delete and roll back — deleting the MIDX file just makes Git slower; it doesn’t lose any data. You can test and roll back freely at any time.
- Works on Windows too — Git for Windows 2.25+ has full support, no additional configuration required.
This isn’t a silver bullet, but it’s an optimization worth enabling immediately if your repo has grown past a few hundred MB and you’re starting to feel lag on everyday read commands.

