Skip to content
LatchkeyLatchkey home

git gc --prune=now --aggressive in CI Caches

git gc repacks objects and prunes ones older than the grace period; git gc --prune=now drops all unreachable objects immediately to reclaim disk on cached runners.

On persistent runners a .git directory grows without bound as branches and rewrites pile up. gc compacts it, and --prune=now reclaims space now instead of after the default two-week grace window.

What it does

git gc consolidates loose objects into packfiles, prunes unreachable objects older than the grace period (default 2 weeks), expires reflogs, and packs refs. --prune=now drops the grace period so all currently-unreachable objects go immediately. --aggressive spends more CPU for a tighter pack; it is rarely worth it routinely.

Common usage

Terminal
git gc
# reclaim everything unreachable right now
git reflog expire --expire=now --all
git gc --prune=now
# quick, only if the auto threshold is hit
git gc --auto
# tighter (slow) repack
git gc --aggressive --prune=now

Options

FlagWhat it does
--prune=<date>Prune unreachable objects older than <date> (now = all)
--no-pruneRepack but keep unreachable objects
--aggressiveSpend extra CPU for a more compact repository
--autoRun only if enough loose objects/packs have accumulated
--quietSuppress progress output

In CI

Reflogs keep dropped commits reachable, so run git reflog expire --expire=now --all before git gc --prune=now to actually free them. Avoid --aggressive in regular CI: it is slow and the gain is marginal. Note that a concurrent git process can race gc and leave a gc.log warning; the next operation usually clears it.

Common errors in CI

"warning: There are too many unreachable loose objects; run 'git prune' to remove them." is a hint, not a failure. "fatal: gc is already running on machine ..." means a stale gc.pid lock from a killed job; remove .git/gc.pid. "error: The last gc run reported the following ... see .git/gc.log" surfaces a prior failure; read gc.log and resolve, then it clears.

Using this in CI

CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.

.github/workflows/ci.yml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0   # history, tags, and git describe all need this

- run: |
    git rev-parse --is-shallow-repository   # expect false
    git rev-parse --abbrev-ref HEAD          # prints HEAD when detached

Frequently asked questions

git gc --prune=now --aggressive in CI Caches?
On persistent runners a .git directory grows without bound as branches and rewrites pile up. gc compacts it, and --prune=now reclaims space now instead of after the default two-week grace window.
What it does?
git gc consolidates loose objects into packfiles, prunes unreachable objects older than the grace period (default 2 weeks), expires reflogs, and packs refs. --prune=now drops the grace period so all currently-unreachable objects go immediately. --aggressive spends more CPU for a tighter pack; it is rarely worth it routinely.
In CI?
Reflogs keep dropped commits reachable, so run git reflog expire --expire=now --all before git gc --prune=now to actually free them. Avoid --aggressive in regular CI: it is slow and the gain is marginal. Note that a concurrent git process can race gc and leave a gc.log warning; the next operation usually clears it.
Common errors in CI?
"warning: There are too many unreachable loose objects; run 'git prune' to remove them." is a hint, not a failure. "fatal: gc is already running on machine ..." means a stale gc.pid lock from a killed job; remove .git/gc.pid. "error: The last gc run reported the following ...

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card