Great work!

XP to next level

BugEater
EN

The Expiry Window and Garbage Collection

Learning Objectives

By the end of this lesson you will be able to:

  • State how long reflog entries and unreachable objects survive
  • Explain what git gc does and when it runs
  • Identify the commands that can destroy recoverable work immediately
  • Choose a permanent anchor when 30 days is not long enough

The Two Clocks

Git keeps two separate expiry settings, and the difference matters:

git config --get gc.reflogExpire             # default: 90 days
git config --get gc.reflogExpireUnreachable  # default: 30 days

90 days for entries that are still reachable — positions on branches that still exist.

30 days for unreachable ones. This is the clock that matters for recovery, because a lost commit is unreachable by definition. Thirty days is the real window.

What git gc Actually Does

Garbage collection does maintenance: it compresses loose objects into pack files, prunes reflog entries past their expiry, and deletes unreachable objects older than the prune threshold (two weeks by default).

You almost never run it. Git runs it for itself, automatically, after enough loose objects accumulate — typically during a commit, merge or pull. That is git gc --auto, and it is deliberately conservative.

The practical consequence is this: during the 30-day window, your lost commits are safe from routine Git usage. Nothing you do in ordinary work will collect them. That is why the recoveries in the previous lesson are so reliable.

The Commands That Break That Promise

Three commands can destroy recoverable work immediately, ahead of any clock.

git gc --prune=now

Collects unreachable objects right now, ignoring the two-week grace period. Any commit that was recoverable a second ago may not be.

git reflog expire --expire-unreachable=now --all

Empties the unreachable reflog entries immediately, removing the IDs you would have recovered from.

git reflog delete HEAD@{5}

Removes one specific entry.

None of these are commands you need in normal work. They appear in Stack Overflow answers about repository size, and they are the reason a recovery that should have worked sometimes does not. Do not run them while anything is unrecovered.

Repositories Where the Window Is Shorter

A few situations shorten the window without anyone deciding to.

A fresh clone has no reflog. Cloning creates a new one with a single entry. Nothing that happened before the clone is in it — which also means a colleague's re-clone erases their recovery history entirely.

Aggressive maintenance settings. Some teams configure shorter expiry on shared build machines. git config --get gc.reflogExpireUnreachable tells you what you actually have.

CI checkouts. Build agents typically use shallow clones with almost no history and no meaningful reflog. Never plan to recover anything from a CI workspace.

When You Need Longer Than 30 Days

The reflog is a safety net, not an archive. If commits genuinely need to survive, give them a permanent reference:

git tag archive/PAY-4417-investigation 4c1f8ab

A tag is a reference, so the commits are reachable — no expiry, no garbage collection, forever. This is the right tool for an investigation branch you might need again next quarter.

git push origin archive/PAY-4417-investigation

Pushing it takes it off your machine entirely, which is the only real protection against a lost laptop or a re-clone.

The same reasoning applies to a branch:

git push origin repro/PAY-4417

An unpushed branch exists in exactly one place. A pushed one exists in two.

The Habit This Adds Up To

Three sentences, and they cover everything in Module 5:

When something goes wrong, run git reflog first. Before anything else, while the entry is near the top.

Anchor what you recover, immediately. git branch or git tag, the moment you find it. An unanchored commit is on a 30-day timer.

Never run pruning commands while anything is unrecovered. Repository size is not urgent. Your colleague's afternoon is.

Pro Tip: If a recovery matters and you are not certain you have found everything, git fsck --lost-found before you clean anything. It lists every dangling object in the repository, so you can check what else is floating loose before you take an action that removes it.

Key Takeaways

  • Reachable reflog entries last ~90 days; unreachable ones ~30 — the second is the recovery window
  • git gc runs automatically and conservatively; normal work will not collect your lost commits
  • git gc --prune=now and git reflog expire --expire-unreachable=now destroy recoverable work immediately
  • A fresh clone starts with an empty reflog, and CI workspaces have no useful history
  • Tags and pushed branches are permanent references — use them when 30 days is not enough
  • Reflog first, anchor immediately, and never prune while something is still lost

Quiz

Which expiry setting governs how long a lost commit stays recoverable?

Will routine work — commits, pulls, merges — garbage-collect commits you are trying to recover?

Which command can destroy recoverable commits immediately?

An investigation branch may be needed again next quarter. How do you keep it safely?