You ran git reset --hard to undo a local experiment and overshot. git log now stops two commits earlier. The files look older. The work you wanted is not in the working tree. Those commits are not deleted yet — Git still keeps a local diary of where HEAD has been. This page is how to read that diary and put the branch back.

Reset modes (--soft, mixed, --hard) live in When Push Fails. Use that page to choose a reset; use this one when a hard reset already went too far. If you still need init, stage, and commit, start with Git Without the Panic.

Warm-up: a local overshoot lab

A single local repo is enough. Create a throwaway folder, make three commits, then reset too far on purpose:

mkdir git-reflog-lab
cd git-reflog-lab
git init
echo 'Project notes' > notes.txt
git add notes.txt
git commit -m "Add notes"
git branch -M main

echo 'Deploy checklist' >> notes.txt
git add notes.txt
git commit -m "Add deploy checklist"

echo 'Rollback plan' >> notes.txt
git add notes.txt
git commit -m "Add rollback plan"
git log --oneline

Three commits, newest first (hashes will differ):

c8e4b71 Add rollback plan
a2d9e03 Add deploy checklist
f1b0c44 Add notes

Now pretend the last two were a mistake:

git reset --hard HEAD~2
git log --oneline

Log now shows only the first commit:

f1b0c44 Add notes

The checklist and rollback commits look gone. Stay inside git-reflog-lab for the rest of the page. Delete the folder when you finish.

Note: —hard also discards uncommitted edits in tracked files. Prefer --soft or mixed until you are sure — details in When Push Fails. This lab commits first so the “lost” work still exists as objects Git can find.

Mental model: reflog is HEAD’s diary

git log walks commits reachable from the current branch. After a hard reset, the branch pointer moved backward, so the dropped commits no longer appear there.

Reflog records every time HEAD moved on this machine: commits, resets, switches. It is a local log in .git/logs/, not part of the history you push. That is why a commit can vanish from git log and still show up in git reflog.

Each line is “HEAD was at this SHA, because of this action.” The newest move is HEAD@{0}. One move ago is HEAD@{1}, and so on. The SHA in the first column is the stable id; the {n} index is only a convenience.

Note: Reflog does not replace a clean branch habit. Keep experiments off the shared default with Stay Off Main so you reset less often in the first place.

Hero use case: find the SHA, then restore

Immediately after the overshoot, ask Git where HEAD has been:

git reflog

Sample output (your hashes will differ):

f1b0c44 HEAD@{0}: reset: moving to HEAD~2
c8e4b71 HEAD@{1}: commit: Add rollback plan
a2d9e03 HEAD@{2}: commit: Add deploy checklist
f1b0c44 HEAD@{3}: commit: Add notes

HEAD@{0} is the reset you just did. The line to recover is the tip from before that reset — here HEAD@{1} / c8e4b71 (Add rollback plan). Inspect before you move anything:

git show 'HEAD@{1}'

You should see the rollback-plan commit message and its diff. Quote the HEAD@{n} form so the shell does not treat the braces as syntax.

Restore the current branch in place

To put main back on that tip:

git reset --hard 'HEAD@{1}'
git log --oneline

All three commits should be listed again:

c8e4b71 Add rollback plan
a2d9e03 Add deploy checklist
f1b0c44 Add notes

You can pass the SHA instead of HEAD@{1} — same result, and the SHA does not shift when you later move HEAD.

Park the commit on a recovery branch

If you would rather inspect without moving main, skip the in-place reset above (or overshoot again with git reset --hard HEAD~2). Copy the SHA from reflog and create a branch there:

git switch -c recover c8e4b71

Use your SHA, not the sample. main stays at the overshot commit. recover points at the restored tip. Compare the two:

git log --oneline -n 3
git switch main
git log --oneline -n 3

On recover you should see the three commits; on main, only Add notes. Keep the recovery branch if you still want those commits; delete it later like any merged feature branch in Stay Off Main.

Note: Once a commit is on a remote and teammates may have it, prefer git revert over reset-and-force-push — same rule as in When Push Fails. Reflog recovery is for work that still lives only on this clone (or that you accept rewriting locally).

HEAD@{n} vs the SHA, and dangling commits

HEAD@{1} means “where HEAD was one recorded move ago.” Run another reset, switch, or commit, and every index shifts. The SHA in the first column does not. After you have moved HEAD again, recover with the hash, not with a remembered {n}:

git reset --hard c8e4b71

Or create the branch from that hash with git switch -c recover c8e4b71 as above.

Until a branch (or tag) points at them again, the dropped commits are dangling: still in the object database, but not reachable from git log on main. Git expires old reflog entries on the order of weeks, then garbage-collects objects nothing points to. Recover sooner rather than later; this is not a forever undo buffer.

Note: You do not need to run expire or git gc yourself for this skill. The useful takeaway is the window: reflog is how you find the SHA while Git still has it.

What reflog cannot do

Reflog is local. A clone that never had the commit has no diary entry and no object to reset to. If you never pushed, another machine cannot recover what only this .git contained.

Never-committed file contents are also out of reach. If you edited notes.txt and ran git reset --hard without committing, there is no SHA for those bytes:

echo 'unsaved thought' >> notes.txt
git reset --hard

The extra line is gone from the file. git reflog still only lists HEAD moves — commits — not unsaved working-tree edits.

Note: Reflog will not help a teammate’s clone, a fresh checkout, or work you never committed. Push (or at least commit) anything you cannot afford to lose; use revert on published mistakes instead of hoping a diary on your laptop is enough.

Quick reference card

GoalCommand
See where HEAD has beengit reflog
Inspect a reflog entrygit show 'HEAD@{1}'
Restore current branch to that entrygit reset --hard 'HEAD@{1}'
Restore using a stable SHAgit reset --hard <sha>
Keep current branch; recover on a new onegit switch -c recover <sha>
Confirm reachable historygit log --oneline --decorate -n 10

Practice drills

Stay inside git-reflog-lab (recreate the three commits if you already cleaned up). Try these without peeking.

  1. Make three commits on main, run git reset --hard HEAD~2, and confirm git log --oneline shows only the first commit.
  2. Run git reflog, restore with git reset --hard 'HEAD@{1}' (or the SHA of the dropped tip), and confirm all three commits are back.
  3. Overshoot with HEAD~2 again. Copy the pre-reset SHA from reflog and run git switch -c recover <sha>. Confirm main is still at the reset tip and recover has the missing commits.
  4. Before restoring, run git show on the reflog SHA (or 'HEAD@{1}') and match the commit message and diff to the work you thought was gone.
  5. After you are back on a known commit, append an uncommitted line to notes.txt, run git reset --hard, and confirm the extra line is gone and reflog has no entry that restores those bytes.

Solid answers — clear and portable, not the only ones:

# 1
echo 'Deploy checklist' >> notes.txt
git add notes.txt && git commit -m "Add deploy checklist"
echo 'Rollback plan' >> notes.txt
git add notes.txt && git commit -m "Add rollback plan"
git reset --hard HEAD~2
git log --oneline

# 2
git reflog
git reset --hard 'HEAD@{1}'
git log --oneline

# 3
git reset --hard HEAD~2
git reflog
git switch -c recover <sha>   # pre-reset tip from reflog
git log --oneline -n 3
git switch main
git log --oneline -n 3

# 4
git show 'HEAD@{1}'
# or: git show <sha>

# 5
echo 'never committed' >> notes.txt
git reset --hard
git reflog
# working tree matches the last commit; the extra line is gone

If those five feel routine, you already have the rescue loop: overshoot, read the diary, inspect the SHA, restore in place or on a recovery branch, and know what reflog will not bring back. Reflog stays a rescue tool, not everyday workflow — the same framing as When Push Fails. Everyday snapshots still start in Git Without the Panic. Keep experiments off the shared default with Stay Off Main. When a bug snuck into history and you need to hunt which commit broke the build, Find the Break is the search skill; this page is only for the moment a reset went too far.