It turns 'I destroyed my work' into a two-minute fix, which makes you braver with history rewriting.
Every time HEAD or a branch moves (commit, reset, rebase, checkout, merge, amend, pull), Git appends an entry to the reflog. It's local only, never pushed, and it is your undo history.
git reflog shows HEAD's log, newest first:
9d2c4e1 HEAD@{0}: reset: moving to HEAD~3
5b7a0f3 HEAD@{1}: commit: Add export button
...HEAD@{1} means "where HEAD was one move ago". git reflog show feature shows a branch's own log, and time-based forms such as main@{yesterday} work too.
Recovery recipes:
- Undo a bad reset or rebase: find the entry from before it, then
git reset --hard HEAD@{4}(or the hash). After a rebase,ORIG_HEADalso points at the pre-rebase tip. - Restore a deleted branch: find its last commit in the reflog (
git branch -Dprints the hash too) andgit branch feature <hash>. - Rescue commits made in detached HEAD: they are in
HEAD's reflog; create a branch at them.
Commits removed from all branches are unreachable but not deleted immediately. Reflog entries expire after 90 days by default (30 days for entries no longer reachable), and git gc then removes the objects. If even the reflog is gone, git fsck --lost-found can find dangling commits.
The limit: the reflog tracks commits. Changes you never committed or stashed, then wiped with reset --hard or restore, are not in it. Commit often and you can always get back.
Example
git reset --hard HEAD~3 # oops, those commits mattered
git reflog -5
# 9d2c4e1 HEAD@{0}: reset: moving to HEAD~3
# 5b7a0f3 HEAD@{1}: commit: Add export button
git reset --hard HEAD@{1} # back to before the reset
git branch -D experiment # Deleted branch experiment (was 41c8e2a).
git branch experiment 41c8e2a # restored
git rebase origin/main # went wrong?
git reset --hard ORIG_HEAD # tip before the rebaseRemember
- Every HEAD/branch movement is logged locally in the reflog
- Recover with git reset --hard HEAD@{n} or git branch name <hash>
- Unreachable commits survive about 30-90 days before gc
- Uncommitted work is not in the reflog
Watch out for
- Expecting the reflog to exist on a fresh clone — it's local to each repository.