After a bulk import
You loaded a large history with git fast-import or a migration tool. The packs it produced are not well optimized, so you run git gc once to rebuild them properly.
Git · Command
git gc does housekeeping on your local repository. It compresses objects into packfiles, throws away unreachable data once it has aged enough, and tidies refs, reflogs, rerere records and worktree metadata so the repository takes less disk space and runs faster.
Last updated
When you run it, git gc triggers several maintenance steps one after another. It runs git repack to gather loose objects and small packs into packfiles, and by default it puts unreachable objects into a separate cruft pack. It prunes unreachable objects older than a grace period (2 weeks by default), runs pack-refs, expires old reflog entries, cleans up rerere data and stale worktrees, and can rewrite the commit-graph. Many everyday porcelain commands call git gc --auto for you. That version only does real work once the repository passes certain thresholds, so running it by hand is rarely needed.
git gc [--aggressive] [--auto] [--[no-]detach] [--quiet] [--prune=<date> | --no-prune] [--force] [--keep-largest-pack]| Name | Type | Description |
|---|---|---|
--aggressive | flag | Optimizes much harder at the cost of far more time. It throws away existing deltas and recomputes them with a larger window and depth. |
--auto | flag | Does nothing unless the configured thresholds show that housekeeping is needed. |
--[no-]detach | flag | Runs in the background (or not), overriding gc.autoDetach. |
--[no-]cruft | flag | Turns on or off storing expiring unreachable objects in a cruft pack instead of as loose objects. On by default. |
--max-cruft-size=<n> | bytes | Upper size limit for new cruft packs. Overrides gc.maxCruftSize. |
--expire-to=<dir> | path | Writes a cruft pack of the pruned objects into this directory. Only has an effect together with --cruft. |
--prune=<date> | date | Prunes loose objects older than this date. The default is 2 weeks ago, set by gc.pruneExpire. |
--no-prune | flag | Prunes no loose objects at all. |
--quiet | flag | Hides all progress output. |
--force | flag | Runs even if another gc may already be running on the repository. |
--keep-largest-pack | flag | Merges every pack except the largest non-cruft pack, .keep packs and cruft packs into one pack. gc.bigPackThreshold is ignored when this is used. |
--auto flag is a cheap check. It quits right away unless thresholds such as gc.auto (about 6700 loose objects) or gc.autoPackLimit (50 packs) have been exceeded.--prune=now removes the safety grace period. If another Git process is writing to the repository at the same moment, its new objects can be deleted.--aggressive is not a free upgrade. It is much slower, and the docs advise against it unless you have benchmarked it on your repository.--auto runs just print that log and stop until it is more than gc.logExpiry old (1 day by default).#!/usr/bin/env bash
set -euo pipefail
# Before: count loose objects and packs
git count-objects -v
# Standard cleanup; unreachable objects younger than 2 weeks are kept
git gc
# Cheap check that only does work if thresholds are exceeded
git gc --auto --quiet
# Keep the big pack and only consolidate the smaller ones
git gc --keep-largest-pack
# Turn off automatic gc for this repository (e.g. during a bulk import)
git config gc.auto 0
# After: compare the numbers
git count-objects -vThis shows a normal gc, a threshold-gated auto run and a run that keeps the largest pack, plus how to disable auto-gc, with object counts printed before and after so you can see the effect.
You loaded a large history with git fast-import or a migration tool. The packs it produced are not well optimized, so you run git gc once to rebuild them properly.
Large blobs were removed from history. Once the reflogs and backup refs that still point at them are gone and the grace period has passed, git gc deletes the objects and the repository shrinks.
On a big repository, run git gc yourself at a convenient time so that auto-gc does not start in the middle of something important.
git gc and git gc --auto, and who calls the second one?Plain git gc always does the full set of housekeeping steps. git gc --auto checks gc.auto (loose object count) and gc.autoPackLimit (pack count) first and stops if neither is exceeded. Common porcelain commands that create objects run it automatically, and once it is triggered it also handles reflogs, rerere data and worktrees.
Follow-up: How do you turn auto-gc off completely? Set gc.auto to 0. That disables the loose-object heuristic and every other --auto heuristic, including gc.autoPackLimit.
By default it only prunes unreachable objects older than 2 weeks (gc.pruneExpire). This protects against concurrent processes that have written an object but have not referenced it yet. Git also keeps anything newer than the prune date plus everything reachable from it, and it refreshes an object's mtime when a command re-adds it.
Follow-up: Is this a complete guarantee? No. The docs say the protections fall short of a full solution, so running commands concurrently still carries a small risk of corruption.
gc keeps every object that is still referenced from anywhere: reflogs, the index, remote-tracking branches or any other ref under refs/*. The deleted branch's commits are probably still in reflogs, and unreachable objects also wait out the 2-week grace period, now inside a cruft pack.
Follow-up: How would you reclaim the space right away on a repository nothing else is touching? Expire the reflogs (for example git reflog expire --expire-unreachable=now --all) and then run git gc --prune=now, accepting that this is only safe when no other process is writing.
git-repackgit-prunegit-refloggit-rereregit-maintenancegit-commit-graphgit-fast-importgit-count-objects