What Is the Reflog?
An advanced, expert-level deep dive into Git's reflog — the private, local safety net that records every place your branches and HEAD have pointed, and why it's often the last line of defense after a risky operation goes wrong.
Every experienced Git user eventually has the same heart-stopping moment: a force-reset, an aggressive rebase, or an accidental branch deletion appears to have wiped out real, uncommitted-feeling work — and for a few seconds, it feels permanently gone. More often than not, it isn’t. The reflog is the quiet, local record that’s been tracking exactly where your branches and HEAD have pointed all along, and it’s frequently the tool that turns a genuine crisis back into a two-minute recovery. This guide goes deep into what the reflog actually is, how it’s maintained internally, what it can and can’t save you from, and how to use it with real confidence rather than as a half-remembered emergency incantation.
1Core Concept: What Is the Reflog?
The reflog, short for “reference log,” is a chronological, local record of every point HEAD and your branch references have pointed to over time. Every time you commit, checkout, merge, rebase, reset, or otherwise move a ref, Git silently appends an entry to the reflog describing exactly what moved, from where, to where, and why.
Think of the reflog like a car’s odometer paired with a trip log — not just tracking the current mileage, but keeping a private, personal record of every place you’ve driven recently, in order, with a note about each trip. If you take a wrong turn and end up somewhere unfamiliar, that trip log lets you retrace your exact steps back to any recent point, even if the road itself gave you no other way to know how you got there.
Picture a concrete scenario: a developer named Wei is cleaning up a messy branch history using an interactive rebase. Midway through, something goes wrong, and the branch ends up in a state that looks nothing like what he intended — commits seem to have vanished entirely. Rather than panicking, Wei opens the reflog, finds the exact entry showing where his branch pointed immediately before the rebase began, and resets back to it — recovering his original state in seconds, even though nothing in his ordinary commit history ever showed that “before rebase” moment directly.
Git deliberately avoids calling this “history” because it isn’t project history in the usual sense — it doesn’t track what changed in your files. It tracks something narrower and more mechanical: where your references have physically pointed, over time, on your specific local machine. That distinction turns out to matter a great deal.
2Why It Exists
Git gives users enormous power to rewrite, discard, and restructure history — resets, rebases, amends, force-deletions. That power is genuinely useful, but it comes with an obvious risk: what happens when you rewrite away something you actually needed? Without some kind of safety net, an accidental hard reset or a botched rebase could mean real, permanent data loss with no recourse at all.
The reflog exists specifically to soften that risk, without getting in the way of Git’s flexibility. Rather than preventing destructive operations outright (which would undermine the very power that makes Git useful), Git quietly keeps its own private trail of where things have recently been — giving users a practical way back from most accidents, without needing to sacrifice the freedom to rewrite history when that’s genuinely what’s wanted.
“If Git already stores every commit as a permanent object in its database, why is a separate reflog even necessary?” — The expected answer: the object database retains commit content, but nothing inherently records which commit a branch pointed to five operations ago once that branch has moved on — without the reflog, that positional history would be effectively invisible and unrecoverable, even though the underlying commit objects technically still exist.
3Architecture & Components
The reflog isn’t one single global list — it’s actually a small collection of separate logs, each tied to a specific reference.
| Component | Role |
|---|---|
| HEAD’s reflog | Records every place HEAD itself has pointed — the most commonly consulted reflog, covering checkouts, commits, resets, and rebases |
| Per-branch reflogs | Each individual branch maintains its own separate reflog, tracking movements specific to that branch alone |
| Reflog entries | Each individual record, storing the old and new commit hash, a timestamp, and a short description of the action that caused the move |
| Local storage location | Reflog data lives entirely inside the local .git directory, specific to that one clone on that one machine |
This per-reference structure matters: HEAD’s reflog and a specific branch’s reflog can tell different stories, since HEAD moves in situations (like a plain checkout to another branch) that don’t necessarily move any particular branch’s own pointer at all.
4Internal Working
Here’s the sequence Git follows internally whenever an operation moves a reference.
An Operation Moves a Ref
A commit, checkout, merge, rebase, reset, or similar operation changes what commit HEAD or a branch currently points to.
Git Captures the Before and After
The old commit hash the ref pointed to, and the new commit hash it now points to, are both recorded together.
A Descriptive Entry Is Appended
Git adds a short, human-readable note describing what kind of operation caused the move — “commit,” “rebase,” “checkout: moving from X to Y,” and so on.
The Entry Is Written Locally
This entry is stored purely inside the local repository’s own .git directory, entirely separate from the commit history itself and from anything ever transmitted to a remote.
Because the reflog tracks the movement of references, not changes to files, it isn’t a general “undo history” for edits to your working directory. It can help you get a branch pointer back to where it used to be — but it cannot recover uncommitted file edits that were never captured as a commit in the first place.
5Data Flow & Lifecycle
flowchart TD
A["Operation moves HEAD\nor a branch ref"] --> B["Old commit hash\ncaptured"]
B --> C["New commit hash\ncaptured"]
C --> D["Descriptive entry\nappended to reflog"]
D --> E["Entry stored locally\nin .git directory"]
E --> F{"Something goes\nwrong later?"}
F -->|"Yes"| G["Reflog consulted to find\nthe commit before the mistake"]
G --> H["Branch reset back\nto that recovered commit"]
F -->|"No"| I["Entry eventually expires\nafter retention window"]
Fig 1 — Every ref movement is quietly logged locally, creating a recovery path if something later goes wrong
The branching path at the bottom captures the reflog’s entire practical purpose: most entries are simply written and quietly age out, unused, forming an invisible safety net that most developers never consciously interact with — until the rare moment they genuinely need it, at which point it becomes indispensable.
6What Gets Logged
Advanced Concepts
A wide range of everyday and advanced Git operations generate reflog entries, each tagged with a description of the specific action.
| Operation | What Gets Recorded |
|---|---|
| Committing | The branch moving from its previous commit to the newly created one |
| Checking out a branch | HEAD moving from the previous branch’s commit to the new branch’s commit |
| Resetting | The branch moving from its prior position to wherever the reset targeted, regardless of mode |
| Rebasing | Each individual step of the rebase, since it internally performs a sequence of commits and resets |
| Merging | The branch advancing to the newly created merge commit |
| Amending a commit | The branch moving from the original commit to the newly amended replacement commit |
Notice that a single high-level command like an interactive rebase can generate many individual reflog entries under the hood — one for each internal step it performs — which is precisely why the reflog can often help recover from a rebase gone wrong at a very specific, granular point, not just from before the whole operation started.
7Reflog vs Commit Log
These two are easy to conflate, but they answer fundamentally different questions, and understanding the distinction clearly is essential to using the reflog correctly.
| Aspect | Commit Log (git log) | Reflog |
|---|---|---|
| What it tracks | The permanent, content-based history of commits and their parent relationships | Where your local refs (HEAD, branches) have physically pointed over time |
| Shared with others? | Yes — pushed and pulled between repositories | No — stays entirely local to one machine, never transmitted |
| Does it expire? | No — commit objects persist until explicitly garbage collected | Yes — individual entries expire after a configurable retention window |
| Primary purpose | Understanding the project’s actual development history | Recovering from local mistakes and accidental ref movements |
The commit log tells the story of the project. The reflog tells the story of your local pointer movements. A commit can appear in the reflog long after it’s disappeared from any branch’s visible commit log — that’s exactly the situation the reflog is built to help recover from.
8Recovering Lost Work
This is the reflog’s signature use case, and understanding the recovery pattern in detail is what makes it genuinely useful under pressure rather than merely theoretical.
Recovering After a Hard Reset
A –hard reset that moves a branch backward doesn’t destroy the commits it moved away from — they remain as valid objects, and the reflog records exactly what the branch pointed to immediately beforehand, providing a direct path back.
Recovering a Deleted Branch
Deleting a branch removes the named reference, but the commit it pointed to is often still findable through HEAD’s reflog (from whenever that branch was last checked out), letting you recreate a branch pointing right back to where the deleted one left off.
Recovering From a Bad Rebase
Because each internal step of a rebase generates its own reflog entry, it’s often possible to reset back not just to “before the rebase started,” but to any specific intermediate point along the way.
Reflog-based recovery only works for content that was actually committed at some point. Uncommitted changes sitting only in your working directory or staging area were never captured as a commit object in the first place, and the reflog — which only tracks ref movements — has nothing to recover for them.
9Retention & Expiration
The reflog is not designed as a permanent, indefinite archive — entries are deliberately allowed to expire over time, and understanding this expiration model is important for anyone relying on it for recovery.
- Reachable entries typically expire after around 90 days by default. Reflog entries for commits still reachable from some branch or tag are generally kept for a fairly generous window before becoming eligible for cleanup.
- Unreachable entries expire much sooner, often around 30 days by default. Once a commit is no longer reachable from any branch or tag at all, its reflog entry — and eventually the commit object itself — becomes eligible for garbage collection considerably faster.
- Expiration happens as part of routine repository maintenance. Git’s periodic garbage collection process is what actually prunes expired reflog entries and any commit objects that have become fully unreachable and unreferenced.
- These windows are configurable. Teams with specific recovery needs can adjust the default retention periods, trading some extra local disk usage for a longer recovery window.
10Advantages, Disadvantages & Trade-offs
Advantages
- Provides a genuine, practical safety net for the vast majority of accidental history-rewriting mistakes
- Requires zero manual effort to benefit from — it’s maintained automatically in the background at all times
- Grants fine-grained recovery, often down to a specific intermediate step within a complex operation like a rebase
- Costs almost nothing in terms of storage or performance overhead
Trade-offs
- Purely local — it offers zero protection for a teammate’s mistake on their own machine, or for anything already pushed and then force-overwritten remotely
- Cannot recover uncommitted working directory changes, only movements between already-committed states
- Entries eventually expire, meaning it’s a safety net with a time limit, not a permanent undo history
- Easy to overlook entirely if a developer isn’t aware it exists, missing out on recovery options during a real crisis
11Security & Privacy Implications
Because the reflog is a detailed, local record of activity, it carries some worthwhile security and privacy considerations, particularly on shared or less-trusted machines.
- The reflog can reveal history that was deliberately rewritten away. If sensitive content was committed and later removed via a history rewrite specifically to eliminate it, the original commit may still be recoverable through the reflog until it fully expires and is garbage collected.
- It never leaves the local machine on its own. Because it’s purely local and never pushed to a remote, the reflog doesn’t expose activity to collaborators or expand the audience with access to it beyond whoever already has access to that specific local clone.
- Fully removing sensitive committed content requires more than a simple history rewrite. A thorough removal typically needs to address the reflog entries and force garbage collection as well, not just amend or rebase the visible branch history.
- Shared or multi-user machines warrant extra caution. On a computer accessed by multiple people, a detailed local reflog is one more place where sensitive project activity could potentially be inspected by someone with access to that machine’s filesystem.
12Diagnostics & Direct Inspection
Git provides direct ways to view and work with reflog data, useful both for routine recovery and for genuinely understanding how a repository’s local state evolved.
Viewing the Full Reflog Chronologically
The reflog can be displayed as an ordered list of entries, each showing the resulting commit, a relative position marker, and the descriptive action that caused that particular movement.
Referencing a Specific Reflog Entry Directly
Git supports referencing a prior ref position directly by its relative position in the reflog, letting you check out, diff against, or reset to that exact historical pointer location without needing to know its full commit hash.
Inspecting Per-Branch Reflogs Individually
Beyond HEAD’s own reflog, each branch maintains its own separate log, useful when trying to trace the specific history of one particular branch’s movements rather than everything HEAD has done across all branches.
13Limits of the Reflog
Understanding what the reflog genuinely cannot do is just as important as understanding what it can, especially in a moment of real panic where false confidence could lead to worse decisions.
- It cannot recover uncommitted work. If changes were never staged and committed, no ref ever pointed to them, and the reflog has no record to offer.
- It cannot help with a fresh clone. A newly cloned repository has no accumulated reflog history at all — it only builds up over time as operations are actually performed locally in that specific clone.
- It cannot recover a teammate’s separate local mistakes. Since it’s entirely local, one person’s reflog has no knowledge whatsoever of another person’s independent local repository or its history.
- It eventually runs out. Once an entry expires and the underlying commit is garbage collected, that specific recovery path is genuinely gone — the reflog is a safety net with real limits, not an infinite undo history.
14Design Patterns & Anti-patterns
Pattern
Check the reflog as a first response to any “I think I just lost work” moment, before assuming the worst.
Why It Works
The overwhelming majority of accidental history-rewriting mistakes are fully recoverable this way — panicking or attempting further, more drastic fixes before checking it often makes the situation genuinely worse.
Pattern
Commit work-in-progress frequently, even with informal messages, before attempting a risky operation like a large rebase.
Why It Works
Since the reflog can only ever recover committed states, frequent, even messy, commits directly widen your practical safety net before doing anything genuinely risky.
Anti-pattern
Treating the reflog as an acceptable substitute for real backups or careful use of destructive commands.
Why It’s Risky
Its retention window is finite and its scope is purely local — relying on it as a primary safety mechanism, rather than a genuine last resort, is a fragile strategy for anything that truly can’t afford to be lost.
Anti-pattern
Assuming that rewriting history to remove sensitive data is complete without also addressing the reflog and forcing garbage collection.
Why It’s Risky
A rewritten branch’s visible history may look clean, while the original, sensitive commit remains fully recoverable through the reflog until it genuinely expires — a false sense of security in exactly the scenario where accuracy matters most.
15Best Practices & Common Mistakes
over “lost” work
altering operations
not a substitute
- Learn to read the reflog before you actually need it. Familiarizing yourself with its output in a calm moment makes it far easier to use effectively under real pressure.
- Commit early and often, especially before risky operations. This maximizes what the reflog is actually able to help you recover, since it only ever restores previously committed states.
- Don’t rely on the reflog as a substitute for genuine backups. Its local-only nature and finite retention window make it a safety net for accidents, not a durable, long-term recovery system.
- Remember its scope is local when debugging “missing” commits on shared branches. If a teammate’s push seems to be missing something, their own reflog — not yours — is what would hold any relevant local history.
- Treat sensitive-data removal from history as an operation that must also account for the reflog. A genuinely thorough removal needs to expire the relevant entries and force garbage collection, not just rewrite the visible branch.
A subtler mistake worth calling out: assuming that because the reflog exists, destructive commands like a hard reset or force push are essentially “safe” to use casually. The reflog meaningfully reduces the consequences of a mistake — it doesn’t eliminate the value of care and deliberate intent when using genuinely history-altering operations in the first place, particularly ones that affect shared, already-pushed branches other people depend on.
16Real-World Usage Patterns
Everyday Accident Recovery
Individual developers most commonly reach for the reflog after an accidental hard reset or an interactive rebase gone sideways, recovering their intended state within minutes.
Built-In Recovery Features
Many graphical Git clients surface reflog-based recovery directly in their interface, letting users browse and restore to prior states without needing to interact with the reflog’s raw output at all.
Teaching Confidence With Destructive Commands
Instructors often introduce the reflog specifically to give learners confidence to actually practice risky operations like rebasing and resetting, knowing genuine mistakes are usually recoverable while learning.
Verifying Sensitive-Data Removal
Teams performing a thorough history rewrite to strip accidentally committed secrets routinely check that relevant reflog entries have also expired, confirming the removal is genuinely complete rather than only superficially so.
17Frequently Asked Questions
18Summary and Key Takeaways
Key Takeaways
- The reflog is a local, chronological record of every place HEAD and your branches have pointed — a Git feature entirely independent of GitHub or any hosting platform.
- It exists to soften the real risk of Git’s powerful, potentially destructive operations, providing a practical recovery path without limiting that underlying flexibility.
- It differs fundamentally from the commit log: the commit log tells the project’s permanent, shared story, while the reflog tracks your own local pointer movements, and quietly expires over time.
- It can recover a surprising range of accidents — hard resets, deleted branches, botched rebases — but only ever for content that was actually committed at some point.
- Retention isn’t indefinite — reachable entries typically last around 90 days, unreachable ones considerably less, both configurable but ultimately finite.
- It carries real security and privacy weight: content removed from visible history through a rewrite may still be fully recoverable through the reflog until it truly expires.
- Used well, it turns “I think I just destroyed my work” from a genuine crisis into a routine, confident two-minute recovery — precisely the safety net that makes Git’s more powerful, history-altering commands practical to use at all.