Repack a Git Repository Safely with git repack
A repository full of loose objects is slow to clone and slow to fsck, and git repack is the tool that tidies it up without touching a single commit.
The route
Jump straight to the step you need, or tick off Done means at the end.
You will finish with a smaller, packed object database, a verification check, and a clear choice between ordinary repacking and cleaning up unreachable objects. The examples match Git 2.43.0 from package git 1:2.43.0-1ubuntu7.3 and its matching git-man package.
Allow about ten minutes. You need a repository you can maintain, enough free disk space for a temporary second pack, and a shell. These commands are normally unprivileged: do not use sudo merely because the repository is large, fix ownership or permissions separately if Git cannot write its object directory.
Checkpoint
Work from the repository root, and make sure no fetch, push, commit or backup job is changing it while you repack.
1. Inspect the repository before changing it
Change to the repository and record its current object counts. This step is read-only:
$ cd /path/to/repository
$ git status --short
$ git count-objects -v
git count-objects -v reports loose objects, objects already in packs, the number of packs, and the approximate packed size. A non-zero count is a reason to consider an incremental repack, not proof anything is wrong. A clean status is useful evidence you are working on the intended checkout, but repacking affects the object database, not tracked files.
Keep this output so you can compare it afterwards. If the repository is bare, skip git status --short and run the count command from its Git directory instead.
2. Run the normal incremental repack
Start with the least surprising form:
$ git repack
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to ... threads
Compressing objects: 100% (...), done.
Writing objects: 100% (...), done.
The exact progress lines vary with repository size and terminal output. Without -a, Git packs only objects not already in a pack, leaving existing packs alone. That makes it a good maintenance step when loose objects have piled up but you do not need to consolidate everything.
Run the count again:
$ git count-objects -v
count: 0
in-pack: 6
packs: 1
The numbers are repository-specific. Look for the loose-object count falling and in-pack rising, not for these exact values.
3. Consolidate packs only when you need to
Use -a when you deliberately want every reachable object packed together. Add -d to remove packs that become redundant and to run git prune-packed:
$ git repack -ad
This changes the object database and can require substantial temporary disk space. It does not rewrite commits or touch the working tree, but removing redundant pack files is not something to run during concurrent fetches or pushes. Schedule it for a quiet window and keep a current backup.
Warning
-ad is a cleanup operation. If the repository holds objects you might still need but that are not reachable from any reference, do not use it casually. Inspect first:
$ git fsck --full --no-reflogs --unreachable
Reflogs can retain otherwise unreachable commits, so excluding them here is intentionally conservative, and only for finding objects outside the reflog view. Do not delete or expire reflogs as part of this guide.
4. Choose a policy for unreachable objects
The flags behave differently, so pick with intent:
- -a -d packs referenced objects and removes redundant packs. Unreachable objects left behind can be removed by normal pruning rules.
- -A -d also repacks all referenced objects, but loosens unreachable objects from existing packs instead of leaving them in an old pack that gets removed immediately. A later
git gccan prune them according to its own expiry rules. - --cruft -d stores unreachable objects in a separate cruft pack. Useful when you want unreachable data to stay subject to normal expiry rather than vanishing with a pack replacement.
Do not reach for these out of habit. Which mode fits depends on whether recovering abandoned commits matters to your retention policy. For a routine first pass, use plain git repack; decide the policy before you reach for -ad or a cruft mode.
5. Limit resource use when necessary
Delta compression trades CPU and memory for smaller packs. Git 2.43.0 defaults to a delta window of 10 and a maximum delta depth of 50. Reduce either if a maintenance job competes with other workloads:
$ git repack -ad --window=5 --depth=25
--window-memory=512m adds a memory limit for the compression window. The manpage warns that actual use gets multiplied by the number of pack-object threads, so set both deliberately on a constrained host:
$ git repack -ad --window=10 --window-memory=512m --threads=2
These values are examples, not universal tuning advice. Measure on a representative clone. -q suppresses progress output but not the work itself; leave progress on for an interactive maintenance run.
6. Verify the result and recover if needed
After any repack, check reachability and compare object counts:
$ git fsck --full
$ git count-objects -v
count: 0
size: 0
git fsck --full should complete without errors. Counts, pack names and sizes vary. A successful repack does not prove a backup is restorable, so test the backup separately.
Recovery
If the command fails for lack of space or gets interrupted, do not remove files from .git/objects/pack by hand. Git normally leaves the old pack in place while writing a new one. Free space, stop competing jobs, and rerun the command. If git fsck --full reports missing or corrupt objects, stop making changes and restore from a known-good backup; do not try random combinations of git gc, git prune and repack flags.
Done means
- You recorded git count-objects -v before and after. So you can prove what changed.
- Loose objects were repacked without touching commits or the working tree.
- You used -a and -d only after choosing an unreachable-object policy.
- The repository was quiet during pack replacement, with enough free space to spare.
- git fsck --full completed without errors.