Git Reflog and Recovering Lost Work

Use the reflog to find and recover commits lost to a bad reset, a deleted branch or a rebase gone wrong.

Lesson 15 of 30Advanced17 min read

Sooner or later, everyone using Git has a moment of panic. You ran git reset --hard and your last three commits disappeared. You deleted a branch with -D before it was merged. A rebase went wrong and your history looks nothing like it did. It feels as though work is gone forever.

Usually, it isn't. Git quietly keeps a diary called the reflog (reference log) that records every time HEAD or a branch tip moves: every commit, checkout, reset, merge, rebase and amend. Commits that no branch points to any more are still stored in the repository for weeks, and the reflog tells you exactly where to find them.

In this lesson you'll read the reflog, undo a hard reset, bring back a deleted branch, rescue work after a bad rebase, and learn the limits of what Git can recover, so you know when to stop worrying and when to be careful.

Reading the reflog

Terminal
git reflog

Each line shows where HEAD pointed after an action, newest first:

Text
1e2f3a4 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
9c8b7a6 HEAD@{1}: commit: Add checkout page
5d4c3b2 HEAD@{2}: commit: Add basket page
  • HEAD@{0} is where HEAD is now, HEAD@{1} where it was one move ago, and so on.
  • The description says what moved it: commit, checkout, reset, merge, rebase or pull.
  • The hash on the left is the commit HEAD pointed to at that moment, and it can be used like any other commit reference.

Undoing a hard reset

git reset --hard moves your branch back and discards changes, but the commits it skipped over still exist. To undo it, find the entry from just before the reset and reset back to it:

Terminal
git reflog
git reset --hard HEAD@{1}

Read the reflog carefully first: the right entry is the one just before the "reset: moving to" line. If you'd rather not move your branch, create a new branch at the lost commit instead: git branch rescued 9c8b7a6.

Restoring a deleted branch

Deleting a branch only removes its label; its commits remain for a while. When you delete with git branch -D, Git even prints the hash: Deleted branch spike (was 0f3e1a9). Recreate the branch from it:

Terminal
git branch spike 0f3e1a9

If you no longer have that message, search the reflog for the last time you were on the branch, for example the commit message or a "checkout: moving from spike" line, and use that hash.

After a bad rebase, and the limits

A rebase records an entry before it starts. If the result is wrong and you've finished the rebase, find the line just before rebase (start) and reset to it, or use ORIG_HEAD, which Git sets to your branch's previous position before risky operations such as rebase, merge and reset: git reset --hard ORIG_HEAD.

The reflog has limits:

  • It's local: it records your repository only, and isn't pushed or cloned.
  • Entries expire, by default after 90 days, or 30 days for commits not on any branch, and garbage collection then removes the commits.
  • Changes that were never committed aren't recorded. Work discarded with git restore or git reset --hard before any commit can't be recovered by Git.

Git Reflog Syntax

Recovery commands

git reflog
git reflog show feature
git reset --hard HEAD@{2}
git branch rescued <hash>
git switch -c rescued HEAD@{3}
git reset --hard ORIG_HEAD

Git Reflog Summary

  • The reflog records every move of HEAD and branch tips in your local repository.
  • HEAD@{n} refers to where HEAD was n moves ago.
  • Undo a hard reset with git reset --hard HEAD@{n} or create a branch at the lost commit.
  • Restore deleted branches with git branch <name> <hash>; ORIG_HEAD points to your pre-rebase position.
  • The reflog is local, expires after weeks, and can't recover changes that were never committed.

Git Reflog Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Undoing an accidental hard reset

Terminal
git reset --hard HEAD~2
git log --oneline -1
git reflog -3
git reset --hard HEAD@{1}
git log --oneline -1
Output
HEAD is now at 9d41e07 Add contact form
9d41e07 (HEAD -> main) Add contact form
9d41e07 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
c3d9e8f HEAD@{1}: commit: Add FAQ section
52ac9e1 HEAD@{2}: commit: Add dark colour scheme
HEAD is now at c3d9e8f Add FAQ section
c3d9e8f (HEAD -> main) Add FAQ section
Line-by-line explanation
  1. Line 1Oops: the last two commits vanish from the branch.
  2. Line 3The reflog still lists them. HEAD@{1} is where main was just before the reset.
  3. Line 4Reset back to that point, and both commits return.
Example 2

Bringing back a deleted branch

Terminal
git branch -D spike
git branch spike 0f3e1a9
git log --oneline spike -1
Output
Deleted branch spike (was 0f3e1a9).
0f3e1a9 (spike) Try a new layout
Line-by-line explanation
  1. Line 1Force-deleting an unmerged branch. Git still prints the hash it pointed to.
  2. Line 2Recreate the branch at that commit.
  3. Line 3The branch and its work are back.
Example 3

Recovering from a bad rebase with ORIG_HEAD

Terminal
git rebase main
git log --oneline -3
git reset --hard ORIG_HEAD
git log --oneline -3
Output
Successfully rebased and updated refs/heads/search-box.
8a3b2f7 (HEAD -> search-box) Style search box
5c1e9d0 Add search box
7e1c0d2 (main) Fix footer link
HEAD is now at 4b9a8c3 Style search box
4b9a8c3 (HEAD -> search-box) Style search box
2d6f1e4 Add search box
9d41e07 Add contact form
Line-by-line explanation
  1. Line 1The rebase finished, but you decide you'd rather have merged.
  2. Line 3ORIG_HEAD points to where the branch was before the rebase.
  3. Line 4The original commits, with their original hashes, are back.

Common Git Reflog Mistakes

Panicking and re-cloning

Don't
rm -rf my-project
git clone https://github.com/samlee/my-project.git   # unpushed commits are gone for good
Do
git reflog
git reset --hard HEAD@{1}

The reflog lives in your local .git folder. Deleting the folder deletes the rescue route.

Resetting to the wrong reflog entry

Don't
git reset --hard HEAD@{0}     # that's where you already are
Do
git reflog                    # read first
git reset --hard HEAD@{1}     # the entry before the bad action

Expecting Git to recover uncommitted edits

Don't
git restore styles.css        # uncommitted edits discarded
git reflog                    # nothing to recover
Do
git stash                     # or commit, before discarding anything

Git Reflog in Real Projects

The reflog as a safety net

Experienced developers rewrite history confidently, with rebases, amends and resets, partly because they know the reflog has their back. Support channels for Git tools are full of "I lost my commits!" questions that end happily with two commands: git reflog and git reset --hard HEAD@{n}.

Git Reflog FAQ

How long does Git keep reflog entries?

By default, entries for commits still reachable from a branch are kept for 90 days, and entries for commits that are no longer reachable for 30 days. After that, git gc can remove them. The settings are gc.reflogExpire and gc.reflogExpireUnreachable if you need longer.

Is the reflog shared when I push?

No. The reflog is a private record of where your own branches and HEAD have pointed on your machine. It isn't pushed or cloned, so a fresh clone starts with an empty reflog and you can't use someone else's to recover their work.

What is the difference between git log and git reflog?

git log follows parent links from a commit, so it shows the project's history. git reflog shows where HEAD or a branch has pointed over time, including commits that no longer appear in any branch's history. That's why the reflog can find commits that git log can't.

How do I see when each reflog entry happened?

Add a date format: git reflog --date=relative shows entries like HEAD@{2 hours ago}, and --date=iso shows full timestamps. git log -g shows the same reflog walk in the full git log format.

Can I use reflog entries with other Git commands?

Yes. Names such as HEAD@{2} work anywhere Git expects a commit: git show HEAD@{2}, git diff HEAD@{1} or git switch -c rescue HEAD@{3}. You can also use times, as in main@{yesterday}, for where main pointed a day ago.

Git Reflog Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the command that shows where HEAD has been.

git 
Multiple choiceeasy

What does HEAD@{2} mean?

Multiple choicemedium

Which of these can the reflog help you recover?

Predict the outputmedium

Git says Deleted branch fix (was 7c6b5a4). Which command restores it?

Multiple choicehard

What does ORIG_HEAD point to after a rebase?

Git Reflog Quiz

5 questions to check what you learned.

  1. Is the reflog pushed to GitHub?

    • No, it only exists in your local repository
    • Yes
    • Only for main
    • Only with --tags
  2. How long do reflog entries last by default?

    • Forever
    • One day
    • About 90 days (30 for commits on no branch) before they can be removed
    • Until you close the terminal
  3. What's the safest way to recover a lost commit without moving your current branch?

    • git reset --hard <hash>
    • git push --force
    • git clean -fd
    • git branch rescued <hash>
  4. What does the reflog record?

    • Only commits
    • Every move of HEAD and branch tips: commits, resets, checkouts, merges and rebases
    • Only pushes
    • Changes in your editor
  5. Why is committing often good insurance?

    • It makes pushes faster
    • Git deletes uncommitted files nightly
    • Only committed work can be recovered through the reflog
    • It reduces repository size