Git Reflog: The Complete Recovery Guide
The reflog is Git's private flight recorder. Every time HEAD moves — commit, reset, rebase, checkout, merge — Git logs it. If you have ever lost work to a git reset --hard or a gone-wrong rebase, the reflog is almost certainly your path back. This guide explains how to read it and how to use it to recover from the most common disasters.
What the reflog actually is
The reference log (reflog) is a local journal stored in .git/logs/. It is separate from your commit history — it records where HEAD (and each branch tip) has pointed over time, including movements that never made it into a permanent commit. Every checkout, every reset, every rebase step creates a new reflog entry.
Critically, the reflog is local only. When you clone a repo you do not get the original author's reflog. This is what makes it a recovery tool: it captures the full history of your local session even when the normal commit graph has been rewritten.
How to read reflog output
Run git reflog (or git reflog show HEAD) to see the HEAD reflog:
git reflogTypical output:
a3f1c29 HEAD@{0}: reset: moving to HEAD~3
9b2e847 HEAD@{1}: commit: add payment validation
4d8c013 HEAD@{2}: commit: extract form component
7f0a1b5 HEAD@{3}: checkout: moving from feature to main
2c9e6d4 HEAD@{4}: rebase (finish): returning to refs/heads/feature
1a7b3f8 HEAD@{5}: rebase (pick): fix edge case in parser
Each line has three parts:
- Hash — the commit SHA that HEAD pointed to at that moment
- HEAD@{N} — a relative selector (0 = current, 1 = one move ago)
- Action + message — what caused HEAD to move and the associated commit message
You can use either the hash or the HEAD@{N} notation in any Git command that accepts a commit reference.
Recovery scenarios
-
Scenario 01You ran git reset --hard and lost commits
The most common panic. You reset to an earlier state and the commits appear gone from git log. Find them in the reflog:
git reflog # find the commit before the reset git reset --hard HEAD@{2} # jump back to that pointOr, if you want to inspect before resetting, check out the target commit first:
git checkout 9b2e847 # detached HEAD — safe to look git checkout -b recovered-work # make it a branch if you want to keep it -
Scenario 02A rebase went wrong and rewrote commits you wanted to keep
After a bad interactive rebase, your branch history is scrambled. The reflog records every step of the rebase. Find the entry labeled rebase (start) — the commit just before it is your pre-rebase branch tip:
git reflog show feature-branch # see the branch-specific reflog git reset --hard feature-branch@{4} # restore to before rebase startedTipRun git reflog show <branch-name> to see the reflog for a specific branch rather than HEAD. This is more precise when you have been on multiple branches in the same session.
-
Scenario 03You deleted a branch with unmerged commits
The branch pointer is gone but the commits still exist. The reflog shows the last commit that was on that branch:
git reflog # find the last commit hash of the deleted branch git checkout -b restored-branch abc1234 # recreate the branch from that hashLook for a reflog entry with an action like checkout: moving from deleted-branch to ... — the hash on that line is the tip of the deleted branch at the moment you left it.
-
Scenario 04You accidentally amended a commit and want the original back
git commit --amend creates a new commit and moves the branch pointer. The original commit is in the reflog:
git reflog # find HEAD@{1} — that's the pre-amend commit git reset --soft HEAD@{1} # go back, keep changes stagedOr, to retrieve just the original commit message without changing your staged changes, use git show HEAD@{1} to read it.
-
Scenario 05You ran git checkout and switched away from uncommitted work
If you switched branches without stashing and Git silently carried your changes — or worse, overwrote them — the reflog tells you where HEAD was:
git reflog # find the checkout entry git diff HEAD@{1} # see what changed when HEAD movedIf Git refused the checkout due to conflicts, your working tree changes are still there. If it succeeded and carried them, git stash then switch back.
Filtering the reflog for large repos
On active repos the reflog can have hundreds of entries. Narrow it down:
git reflog --since="2 hours ago" # entries from the last 2 hours
git reflog | grep "commit:" # only lines where a commit happened
git reflog | grep "reset" # find all resets
git log -g --oneline # reflog in log format — easier to scanHow long does the reflog keep entries?
By default, Git keeps reflog entries for 90 days (30 days for unreachable commits). After that, git gc can prune them. You can inspect the current expiry settings with:
git config --get gc.reflogExpire # default: 90 days
git config --get gc.reflogExpireUnreachable # default: 30 daysTo give yourself more runway on a repo where you do a lot of destructive operations:
git config gc.reflogExpire "180 days"
git config gc.reflogExpireUnreachable "60 days"The reflog is local. If you clone a repo on a new machine or someone else clones yours, the reflog does not transfer. For commits you absolutely cannot lose, push to a remote branch — that is the only off-machine backup.
Quick reference
| Command | What it does |
|---|---|
| git reflog | Show the HEAD reflog (most recent first) |
| git reflog show <branch> | Show the reflog for a specific branch |
| git log -g --oneline | Reflog in compact log format |
| git reflog --since="1 hour ago" | Filter reflog to recent entries |
| git reset --hard HEAD@{N} | Restore HEAD to the state N moves ago |
| git checkout -b name <hash> | Create a branch at any reflog commit hash |
| git show HEAD@{N} | Inspect any reflog entry without moving HEAD |
30+ scenarios. Every reset, revert, reflog, and stash pattern documented with context on when to use each. One PDF, buy once.
Get Git Unfucked →