reflog & recovering lost work
How git reflog records every place HEAD has pointed so you can recover commits that seem lost after a bad reset, deleted branch, or botched rebase.
What you'll learn
- Why commits that become unreachable after reset --hard are almost never truly deleted — and how reflog proves it
- How to read git reflog output and identify the SHA of the state you want to restore
- The difference between resetting the current branch back to a lost commit vs. creating a safe recovery branch
Before you start
Why commits are not deleted when they become unreachable
Every time you run git commit, Git writes a permanent snapshot object to the object store inside .git/objects. That object does not disappear when you run git reset --hard, delete a branch, or rebase. What changes is merely the pointer — the branch label or HEAD — that told Git which commit to show you. The commit itself sits in the object store until Git’s garbage collector decides to prune it, which by default happens no sooner than 30 days for unreachable commits and 90 days for entries in the reflog.
The technical term for a commit that no label points to is an orphaned or unreachable commit. It is invisible to normal commands like git log, but it has not been erased.
What the reflog is
The reflog (reference log) is a local journal that records every position HEAD has occupied, in reverse-chronological order. Every commit, reset, checkout, rebase step, merge, and branch switch appends a new line. The entries are numbered HEAD@{0} (most recent), HEAD@{1}, HEAD@{2}, and so on, where the number in braces is written as HEAD@{n} in backtick notation throughout this lesson.
Each entry stores the SHA of the commit HEAD pointed to at that moment, the operation that moved it, and a timestamp. This makes the reflog your personal time-machine for a repository — one that works even when no remote exists.
git reflog
a3f9c12 HEAD@{0}: reset: moving to HEAD~3
e87b504 HEAD@{1}: commit: Add payment retry logic
c4d2a91 HEAD@{2}: commit: Wire up Stripe webhook
1f0e38a HEAD@{3}: commit: Scaffold checkout controller
8ab5d07 HEAD@{4}: checkout: moving from main to feature/checkout
8ab5d07 HEAD@{5}: pull: Fast-forward
3c21e60 HEAD@{6}: commit: Update README
Reading this output: the bad reset --hard is at HEAD@{0}. The three commits you thought you lost are at HEAD@{1}, HEAD@{2}, and HEAD@{3}. The topmost of those — the one that was your branch tip before the reset — is e87b504.
The recovery loop
Step 1 — find the SHA
Run git reflog and scan for the last good state. Look at the description column: commit: entries name the commit message, and reset: or checkout: entries describe the operation. You want the SHA of the commit that was HEAD just before things went wrong.
If the output is long, pipe it through a pager:
git reflog | head -20
You can also inspect a specific entry before committing to any recovery action:
git show e87b504
Step 2 — choose your recovery method
Option A — move the current branch back (destructive, only if certain)
git reset --hard e87b504
This repoints the current branch to e87b504 and restores the working directory to match. It is the direct inverse of the bad reset. Use this only when you are sure e87b504 is exactly where you want the branch to be.
Option B — create a recovery branch (safer, recommended)
git switch -c recover-branch e87b504
# or, with older Git:
git checkout -b recover-branch e87b504
This creates a new branch at e87b504 without touching your current branch at all. You can inspect the recovered work, cherry-pick individual commits, or merge — without any risk of making the situation worse. This is the safer default whenever you are not 100% certain of the target SHA.
Reflog for a specific branch
git reflog without arguments shows HEAD’s history. To see what happened to a specific branch pointer (useful when you deleted the branch entirely), use:
git reflog show feature/checkout
The output format is the same: SHA, operation, message. Find the SHA of the last commit on that branch and recover it with git switch -c as above.
Last resort: git fsck
If the reflog has expired or you are working in a fresh clone, git fsck can scan the entire object store for commits that are no longer reachable from any reference:
git fsck --lost-found
Dangling commits are written as files under .git/lost-found/commit/. You can inspect them with git show <sha> and then use git switch -c to recover whichever you need. This is slower and noisier than reflog, but it reaches further back.
Quick reference
| Situation | Command |
|---|---|
| See HEAD’s full movement history | git reflog |
| See a specific branch’s history | git reflog show feature/checkout |
| Inspect an entry before recovering | git show HEAD@{3} or git show e87b504 |
| Move current branch back (certain) | git reset --hard e87b504 |
| Create a safe recovery branch | git switch -c recover-branch e87b504 |
| Scan for all orphaned commits | git fsck --lost-found |