open atlas
↑ Back to track
Git, from zero to senior GIT · 04 · 03

Reflog and recovery

git reflog records every move of HEAD locally — commits, resets, checkouts, rebases — so you can find and recover work after a bad reset --hard or a deleted branch. Entries expire after about 90 days; git fsck --lost-found finds dangling commits as a last resort.

GIT Middle ◷ 16 min
Level
FoundationsJuniorMiddleSenior

You run git reset --hard to clean up, and a heartbeat later realise you just threw away three commits of finished work. git log shows no trace of them — to a beginner they look gone forever, and panic sets in. They are not gone. Git keeps a private journal of everywhere HEAD has ever been, and that journal — the reflog — is how you walk right back to the commit you “lost.”

By the end of this lesson you will be able to read the reflog, find a commit that vanished from git log, and restore it — and you will trust that committed work is almost never truly lost.

Goal

After this lesson you can use git reflog to find a commit dropped by a bad reset or a deleted branch, recover it with git reset --hard <sha> or git switch -c, and explain when reflog entries expire and how git fsck finds dangling commits as a last resort.

1

The reflog records every move of HEAD, locally, even moves that rewrite history. Every time HEAD changes — a commit, a reset, a checkout/switch, a merge, each step of a rebase — git appends a line to the reflog with the old position, the new position, and what caused it:

git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# 9f8e7d6 HEAD@{1}: commit: Add retry logic
# 1234abc HEAD@{2}: commit: Add backoff
# ...

HEAD@{0} is where you are now; HEAD@{1} is one move ago, and so on. Crucially, 9f8e7d6 is still listed even though git log no longer shows it — the reflog remembers the position before the reset.

2

To recover from a bad reset --hard, find the pre-reset SHA in the reflog and reset back to it. The reflog line just above the reset entry is the commit you were on before you wrecked things:

git reflog
# ... HEAD@{0}: reset: moving to HEAD~3   <- the mistake
# 9f8e7d6 HEAD@{1}: commit: Add retry logic  <- where you want to be
git reset --hard 9f8e7d6      # or: git reset --hard HEAD@{1}

Your three commits reappear in git log and the working tree is restored. You undid the undo. (Use HEAD@{1} syntax or the raw SHA — both name the same commit.)

3

The reflog also rescues a deleted branch — recover it into a new branch. When you delete a branch, its commits become unreferenced but still exist in the object store, and the reflog kept the tip SHA:

git reflog                       # find the deleted branch's last commit, e.g. c0ffee1
git switch -c rescue c0ffee1     # re-create a branch pointing at it

git switch -c rescue c0ffee1 creates a fresh branch rescue at that commit, pulling the “lost” line of work back into a named, safe place. From there you can rename or merge it as if nothing happened.

Why this works

Why does the work survive a reset --hard at all? Because git almost never deletes commit objects immediately. A reset just moves a pointer; the commit it pointed at becomes “unreachable” but stays in .git/objects. The reflog holds a reference to it, which both lets you find it and stops garbage collection from removing it. This is why “I committed it” is the magic phrase: once work is in a commit, a pointer somewhere almost always still leads back to it.

4

Reflog entries expire, and git fsck is the last resort when no reflog points at the commit. Reflog entries are not kept forever: reachable entries expire after about 90 days, and entries for unreachable commits after about 30 days (gc.reflogExpire / gc.reflogExpireUnreachable). After that, git gc may finally delete the objects. If a commit has fallen out of the reflog but garbage collection has not yet removed it, you can still hunt for it:

git fsck --lost-found      # lists dangling commits not reachable from any ref
git show <dangling-sha>    # inspect a candidate before recovering it

fsck reports dangling commits — objects with no reference pointing at them. It is messier than the reflog (no helpful messages, just SHAs) but it can surface work the reflog has already forgotten.

Worked example

Get back two commits a colleague “deleted” with a hard reset.

A teammate ran git reset --hard origin/main to discard a botched merge, not realising two of their own unpushed commits were on the branch. git log shows them gone. You recover them:

git reflog
# e1e1e1e HEAD@{0}: reset: moving to origin/main
# bbbb222 HEAD@{1}: commit: Cache invalidation fix   <- want this
# aaaa111 HEAD@{2}: commit: Add cache layer
git switch -c recovered-work bbbb222
git log --oneline -2
# bbbb222 Cache invalidation fix
# aaaa111 Add cache layer

Both commits are alive on a new branch recovered-work. Nothing was ever actually deleted — the reset only moved the branch pointer, and the reflog remembered exactly where it had been. The teammate cherry-picks or merges those commits back where they belong, having lost zero work.

Common mistake

The reflog is local and per-clone — it lives in .git/logs/ and is never pushed or fetched. So it cannot rescue work that existed only on a machine you no longer have, and a fresh git clone starts with an empty reflog. The reassurance “committed work is almost never lost” assumes the original repository (with its reflog and object store) still exists. The corollary: push important work to a remote, because that is the one copy the reflog cannot give you back.

Check yourself
Quiz

You ran `git reset --hard HEAD~3` and your three commits vanished from `git log`. What is the most direct way to get them back?

Recap

git reflog is git’s local journal of every HEAD movement — commits, resets, checkouts, rebases. When a reset --hard or a deleted branch makes work disappear from git log, the reflog still references the lost commit: find its SHA and git reset --hard <sha> or git switch -c rescue <sha> to restore it. Entries expire (~90 days reachable, ~30 unreachable), after which git gc may remove the objects; git fsck --lost-found is the last-resort hunt for dangling commits. The key reassurance: once work is committed, it is almost never truly lost — but the reflog is local, so push anything you cannot afford to lose. Next you will start intentionally rewriting history with git commit --amend and interactive rebase.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 4 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.