By admin , 28 April 2026

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

  1. Packs loose objects into pack files.
  2. Re-deltifies if asked.
  3. Removes unreachable objects older than the prune-expire window.
  4. Expires reflog entries past their TTL.
  5. 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 replace mappings.
  • 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.