Introduction
Garbage collection (gc) keeps your repository compact and fast. Git runs it automatically when certain thresholds are exceeded. You can also invoke it manually.
What gc does
- Packs loose objects into pack files.
- Re-deltifies if asked.
- Removes unreachable objects older than the prune-expire window.
- Expires reflog entries past their TTL.
- Optionally writes the commit-graph file.
git gc
git gc --auto
git gc --aggressive
git gc --prune=now
git count-objects -v
Auto-gc thresholds
git config gc.auto 6700 # loose objects threshold
git config gc.autoPackLimit 50 # number-of-packs threshold
git config gc.autoDetach true # run in background
Auto-gc fires after operations that create new objects, like commit and fetch. With autoDetach, it runs in the background and Git returns immediately.
Reflog expiration
git config gc.reflogExpire 90.days
git config gc.reflogExpireUnreachable 30.days
git reflog expire --expire=90.days --all
Reachable entries last 90 days, unreachable 30 by default. Branches and tags you delete leave reflog traces that survive within that window.
Prune window
git config gc.pruneExpire 2.weeks.ago
Unreachable objects older than this are eligible for deletion during gc. --prune=now deletes immediately; useful but dangerous since it removes safety margins.
Aggressive vs normal
git gc # routine: pack new objects, prune old
git gc --aggressive # rebuild deltas from scratch
Aggressive gc is rarely worth it; it is CPU-heavy and the gain is usually small. Normal gc is sufficient for most repos.
Maintenance
Modern Git (2.29+) has a maintenance subsystem that consolidates and improves gc:
git maintenance start
git maintenance run --task gc
git maintenance run --task commit-graph
git maintenance run --task incremental-repack
git maintenance start registers a background job (cron, launchd, or systemd timer) that periodically optimizes the repo.
Locked operations
If gc is interrupted, you may see a stale lock file:
ls .git/gc.pid
rm .git/gc.pid # only after confirming no gc is running
What gc never deletes
Several object classes are exempt from gc until their reference expires:
- Objects reachable from any ref under
refs/. - Objects reachable from the reflog of any ref (until reflog expiry).
- Objects under
refs/stash(the stash list). - Objects pinned by
git replacemappings. - Objects reachable from worktrees' HEAD and index.
git fsck --unreachable --no-reflogs
git reflog expire --expire-unreachable=now --all
git gc --prune=now
That last sequence is the standard "really delete unreachable history" recipe; reach for it carefully.
Common mistakes
Running git gc --prune=now right after a destructive operation, eliminating the reflog safety net. Always wait the default window unless you really need the space. Disabling auto-gc on a fast-moving repo, accumulating millions of loose objects. Running --aggressive on a schedule under the assumption that "more is better"; it just burns CPU. Finally, running gc concurrently with git push on a server; modern Git handles concurrency safely, but custom scripts that fight over .git/objects can corrupt. Use git maintenance rather than home-grown cron jobs.