How long do Git reflog entries survive, and which config keys control their expiry?
answer
- Two windows, not one
- The shorter one governs the entries you actually need
- Nothing expires on a timer — a command does it
- Both keys accept never
- Clearing journals is a prerequisite for pruning objects
basics
~10 sReflog entries are pruned during garbage collection: gc.reflogExpire drops entries older than 90 days by default, and gc.reflogExpireUnreachable drops entries for commits unreachable from the ref after 30 days. Both accept never.
solid answer
~50 sThe reflog is a rolling window, not an archive. Expiry is performed by `git reflog expire`, which `git gc` runs as part of its work (including the automatic gc that fires after enough loose objects accumulate). Two settings define the window: `gc.reflogExpire`, default **90 days**, prunes old entries generally, and `gc.reflogExpireUnreachable`, default **30 days**, prunes entries whose commits are no longer reachable from the ref's current tip — precisely the entries you would need after a bad reset. So the practical safety net for lost work is about a month, not three. Both accept `never` to disable pruning, and both can be scoped per ref pattern, e.g. `gc."refs/stash".reflogExpire`. To go the other way and deliberately drop the history, `git reflog expire --expire=now --all` followed by `git gc --prune=now` clears the journals and lets unreachable objects go.
code
ini · 3 lines[gc]
reflogExpire = 90.days.ago
reflogExpireUnreachable = 30.days.agogo deeper
Know the reflog is temporary rather than permanent: entries are cleaned up after weeks, so it is an immediate-undo tool, not an archive.
Name gc.reflogExpire at 90 days and gc.reflogExpireUnreachable at 30, and explain that unreachable entries are the ones recovery depends on.
Show operational nuance: expiry runs with gc rather than on a timer, extending the window trades disk for safety, and purging requires clearing journals before pruning objects.
Own the policy: retention windows on shared or large repositories are a size-versus-recoverability tradeoff, and any guarantee about recovering deleted work has to be provided by pushed refs, not by local expiry settings.
## The reflog is a rolling window A reflog entry keeps a commit *addressable*, and while a commit is addressable garbage collection will not delete it. That is what makes the reflog a safety net. It also means an unbounded reflog would keep every discarded commit alive forever, so Git prunes it. ## The two windows - **`gc.reflogExpire`** — entries older than this are removed. Default **90 days**. - **`gc.reflogExpireUnreachable`** — entries whose commits are **not reachable** from the ref's current tip are removed after this shorter period. Default **30 days**. The distinction is the important part. An entry recording an ordinary commit that is still part of the branch's history is reachable, and it lives for the long window. An entry recording the tip you abandoned with `git reset --hard` or a pre-rebase position is unreachable, and it lives for the short one. Since unreachable entries are exactly the ones recovery depends on, the honest answer to "how long do I have to recover a lost commit?" is **about 30 days by default**, not 90. Both values accept a date specification such as `never`, `now`, `90.days.ago`, or `1.year`. And both can be scoped to a ref pattern with the middle-section form `gc.<pattern>.reflogExpire`, which is how people give particular refs a different policy from the repository default. ## When pruning actually happens Expiry is not a background timer. It runs when `git reflog expire` runs, which in practice means when `git gc` runs — either invoked explicitly or triggered automatically by commands that create objects once enough loose objects have accumulated. Two consequences: - In a quiet repository, entries can outlive their nominal window simply because nothing has triggered a gc. - In a busy one, an automatic gc can close the window right on schedule. Never treat "it was only three weeks ago" as a guarantee. ## Extending the window If a repository holds work you want to keep recoverable for longer, set the values explicitly: `git config gc.reflogExpire never` `git config gc.reflogExpireUnreachable never` That keeps journals forever, at the cost of unbounded reflog files and objects that can never be pruned — the repository grows and never shrinks. It is a reasonable choice for a personal repository doing heavy history surgery, a poor default for a large shared one. ## Closing the window deliberately The reverse operation matters too. Because reflog entries keep objects alive, dropping a branch is not enough to make its commits collectable. The explicit sequence is: `git reflog expire --expire=now --all` `git gc --prune=now` The first clears all journals immediately; the second collects objects that are now unreachable. This is the mechanism behind "the old commits are still there after I removed them" — until both steps run, the reflog is still pointing at them. ## What expiry does not do Expiring an entry removes the *pointer*, not the object. If another ref still reaches the commit — a tag, another branch, a remote-tracking ref — it survives regardless. Conversely, once both the entry and every other reference are gone and gc has pruned, the object is genuinely deleted and no configuration recovers it. ## What to say in an interview Name both keys with their defaults, explain why the 30-day unreachable window is the number that matters for recovery, note that expiry is tied to gc rather than to wall-clock time, and mention the two-command sequence for deliberately purging. A candidate who says "the reflog keeps things for 90 days" without the unreachable distinction has the shape right and the operational detail wrong.
- Why is 30 days the number that matters rather than 90?Because recovery almost always concerns commits that became unreachable — the tip abandoned by a hard reset, the pre-rebase position. Those entries fall under `gc.reflogExpireUnreachable`, whose default is 30 days. The 90-day `gc.reflogExpire` window mostly covers entries for commits still reachable from the branch, which you could find with `git log` anyway.
- What is the cost of setting both expiry values to never?Journals grow without bound and, more importantly, every commit they name stays reachable, so garbage collection can never reclaim abandoned objects. The repository only grows. It is a defensible choice for a personal repository doing heavy history rewriting; on a large shared repository it turns every discarded experiment into permanent disk usage.
saying these in an interview costs you the question
- Saying the reflog keeps everything for 90 days full stop
- Believing expiry happens automatically on a wall-clock schedule
- Assuming deleting a branch immediately makes its commits collectable
- Thinking expiring an entry deletes the commit object outright
- Expecting reflog settings to apply to other clones of the repository