Why is Git's reflog not a substitute for a backup or a team-wide safety net?
answer
- Ask what travels between repositories
- Ask what survives a month
- Ask what exists on the server
- Objects can be recovered; unstaged edits never were
- Durability is a property of refs, not journals
basics
~20 sThe reflog is per-repository local state: never pushed, fetched, or cloned, pruned after weeks, absent from bare repositories by default, and covering only refs that moved in that one working copy. It dies with the disk it sits on.
solid answer
~50 sThe reflog is an excellent personal undo buffer and a poor guarantee. Four limits define it. It is **local**: journals are never transmitted by push, fetch or clone, so a fresh clone starts with a single `clone:` entry and nobody else's reflog can rescue your branch. It **expires**: `gc.reflogExpireUnreachable` defaults to 30 days for exactly the unreachable entries recovery needs, and pruning happens whenever gc runs. It is **narrow**: it records ref movements in that one repository, so it says nothing about a branch you never had, and content that was never committed — working-tree edits, untracked files — was never an object and cannot be recovered from any journal. And it is **absent where you would want it most**: `core.logAllRefUpdates` defaults to false in a bare repository, so a server-side repo keeps no reflogs unless someone enables it. Durable safety comes from pushed refs and real backups.
go deeper
Understand the reflog only helps in the repository where the mistake happened, only for a while, and only for work that was committed at some point.
Be able to list the concrete limits — never transferred, pruned by gc, off by default in bare repositories, ref movements only — rather than just calling it temporary.
Turn the limits into practice: push work-in-progress, create a real backup branch before rewriting history, and never quote a recovery window you cannot guarantee.
Own the distinction between an individual undo buffer and organisational durability, and decide where checkpoints, mirrors, and server-side ref logging belong when history rewrites are routine.
## The claim under examination "Don't worry, the reflog has your back" is true for a developer who mistyped a reset ten minutes ago and false as a team-level guarantee. A lead is expected to know exactly where the line falls, because the difference decides whether history rewrites need a separate safety procedure. ## Limit one: it is local, always Reflogs are per-repository files. They are not part of the object database, not carried by any refspec, and not transferred by `git push`, `git fetch`, or `git clone`. Concretely: - A fresh clone's HEAD journal contains one entry, `clone: from <url>`. The project's decade of history is present as commits; none of it is present as reflog. - Your colleague cannot inspect your journal to see what you did, and you cannot inspect theirs. - A CI checkout, a container build, an ephemeral workspace — each has an empty-for-practical-purposes reflog and is discarded afterwards. So the reflog protects exactly one thing: mistakes made in the working copy where the reflog lives, recovered by the person sitting at it. ## Limit two: it expires, on gc's schedule `gc.reflogExpire` defaults to 90 days and `gc.reflogExpireUnreachable` to 30. The second is the operative one, because the entries recovery needs are precisely those whose commits are no longer reachable. Pruning is performed by `git reflog expire`, which runs as part of `git gc` — explicitly or via the automatic gc that fires once enough loose objects accumulate. That makes the actual retention non-deterministic: longer than the nominal window in an idle repository, right on time in a busy one. No promise of the form "we can always get it back within N weeks" is safe. ## Limit three: it records ref movements, nothing else The journal stores old and new object ids. Anything that never became an object is outside it forever: - Uncommitted working-tree edits destroyed by `git reset --hard`. - Untracked and ignored files deleted by `git clean -fdx`. - Any change made in an editor and never staged. The most commonly "lost" work in practice falls in this category, and no reflog, and no `git fsck`, can produce it. ## Limit four: it is off where you would most want it `core.logAllRefUpdates` defaults to **true** in a repository with a working tree and **false** in a bare one. The bare repositories that serve as shared endpoints and mirrors therefore keep no journals at all by default. If you want a server-side record of how refs moved — who force-updated what, and to which id — you have to enable it deliberately on that repository. It is also worth remembering that the default journalled namespaces are HEAD, `refs/heads/*`, `refs/remotes/*` and `refs/notes/*`: tags are not journalled, so a moved tag leaves no local trace of its previous target. ## What to build instead If recoverability matters, make it a property of refs rather than of journals: - **Push early and often.** A commit that exists in another repository is durable in a way no local journal is. Personal work-in-progress refs pushed under a namespace of your own cost nothing. - **Create explicit checkpoints before risky operations.** `git branch backup/pre-rewrite` or a tag is a real ref: visible, transferable, and never overwritten by the next operation the way `ORIG_HEAD` is. - **Back up the repository, not the reflog.** A mirror clone plus filesystem backups protect against the case the reflog cannot touch at all: the disk failing or the directory being deleted. - **Enable ref logging on mirrors deliberately** if you want an on-server record, and treat it as observability rather than as backup. ## The judgment to demonstrate Say clearly what the reflog *is* — a local, expiring, per-ref journal that makes individual mistakes cheaply reversible — and then say what it is not: it is not replication, not retention, not an audit log, and not a backup. Teams that rely on it for the second list discover the gap during an incident, when the affected clone has already been reset, re-cloned, or thrown away.
- What would you put in a team runbook for a routine history rewrite?Create an explicit checkpoint before starting — `git branch backup/pre-rewrite` or a tag — and push it, so the pre-rewrite tip exists as a real ref in more than one repository. Verify the result against that ref, then delete it. This depends on nothing local, survives any number of subsequent operations, and is visible to everyone, unlike a reflog position.
- Is there value in enabling ref logging on a shared bare repository?Yes, as observability rather than backup. Setting `core.logAllRefUpdates` to true there makes ref updates on that repository inspectable, so you can see what a ref pointed at before a force update. It is still local to that machine, still subject to expiry, and still no substitute for backups or for the objects existing in other clones.
- Why can't the reflog help with the most common kind of lost work?Because the most common loss is uncommitted: editor changes wiped by `git reset --hard`, or untracked files removed by `git clean -fdx`. The reflog records movements of refs between object ids, and that content never became an object. Committing or stashing before risky commands is the only thing that brings it into Git's reach at all.
saying these in an interview costs you the question
- Treating the reflog as a repository backup
- Assuming a teammate or the server can read your reflog
- Believing recovery is possible indefinitely after a mistake
- Expecting a bare server repository to journal ref updates by default
- Claiming the reflog can restore uncommitted or untracked files