Nearly every "I've lost my work in git" panic is unnecessary. git reflog records every position HEAD has ever had — every commit, reset, rebase, checkout, and amend — and keeps the record for about 90 days. It is the undo log under all of git's other undo commands.
Reading the log #
$ git reflog
9a8b7c (HEAD -> main) main@{0}: reset: moving to HEAD~1
f3e2d1c main@{1}: commit: fix: cache headers on media
b2c3d4e main@{2}: rebase (finish): refs/heads/feature/toc onto main
Each line is one movement: where HEAD ended up, which operation moved it, and an index. main@{1} means "where main pointed one move ago" — and every one of those positions is still a live commit you can show, diff, or check out:
$ git show f3e2d1c # it's all there
$ git switch -c rescue f3e2d1c # branch from it: recovered
The rescues that pay for learning this #
The hard reset you regret:
$ git reflog # find the commit before the reset
$ git reset --hard main@{1}
The branch you deleted:
$ git reflog
b1c2d3 HEAD@{4}: checkout: moving from feature/toc to main
$ git branch feature/toc b1c2d3^ # the tip commit, rebranched
The rebase that went sideways:
$ git reflog # find "rebase (start)" line
$ git reset --hard HEAD@{2}
In every case the pattern is identical: find the last good position in the reflog, then branch from it or reset to it.
Why this is safe to rely on #
Git never deletes reachable commits — and reflog keeps unreachable ones alive for gc.reflogExpire (90 days default) precisely so mistakes stay repairable. The record is per-ref (git reflog main, git reflog feature/x) as well as for HEAD, and it lives in .git/logs/ — local only, never pushed, which is also why it cannot save work that was never committed.
| Lost thing | Rescue |
|---|---|
| Commit after reset --hard | reflog + reset or branch |
| Deleted branch | reflog of that branch + git branch |
| Pre-rebase state | reflog + reset to before "rebase (start)" |
| Dropped stash | reflog of stash — fragile, act fast |
| Uncommitted work, file deleted | Not reflog: filesystem/editor backup only |
The one real limit #
Reflog records commits, not working-tree changes. git reset --hard discards uncommitted edits permanently; reflog brings back only what was committed somewhere. The commit early habit is what makes reflog omnipotent in practice — more checkpoints, more rescue points.
The reset it rescues is reset vs revert; the archaeology it supports includes bisect. The Git series continues.
