git reflog is your nuclear option for recovering lost work, and it’s surprisingly simple once you see it in action.

Let’s say you’ve just done some experimental work, maybe a few commits, and then you decide to rebase your branch. During the rebase, something goes wrong, or you accidentally reset your branch to an older commit, and suddenly your recent work seems to have vanished. Panic sets in.

Here’s how git reflog saves the day. First, you’ll want to see what Git has been tracking. Run git reflog.

$ git reflog
a1b2c3d HEAD@{0}: rebase (finish): returning to refs/heads/main
e4f5g6h HEAD@{1}: commit: Add feature X
i7j8k9l HEAD@{2}: commit: Refactor component Y
m0n1o2p HEAD@{3}: rebase (start): rebasing main onto origin/main
q3r4s5t HEAD@{4}: checkout: moving from feature-branch to main

Each line in the git reflog output represents an action that changed the state of your HEAD (your current position in the commit history). The output shows you the commit hash (the short a1b2c3d is a reference to the full hash), a human-readable description of the action, and a HEAD@{n} reference indicating how many steps back that state was.

The key insight here is that Git doesn’t actually delete commits immediately when you rewrite history (like during a rebase or reset). It just makes them unreachable from your current branch. git reflog provides a temporary lifeline to these "lost" commits by showing you where HEAD has been.

To recover those lost commits from our example, you’d look for the commit before the problematic rebase or reset. In this case, it’s e4f5g6h (the commit "Add feature X"). You can then reset your branch back to that specific commit.

$ git reset --hard e4f5g6h

The --hard flag is crucial here. It tells Git to move your current branch pointer (HEAD) to the specified commit and to update your working directory and staging area to match that commit. This effectively "rewinds" your branch to that point, bringing back your lost commits.

Why does this work? Git maintains a log of where HEAD has pointed. This log, the reflog, is local to your repository. When you perform operations that move HEAD (like commits, resets, merges, rebases, checkouts), Git records the old HEAD and the new HEAD in this log. Even if you rewrite history and make commits appear gone, the reflog entry still points to the commit object, allowing you to retrieve it.

Now, what if you want to bring those commits back onto a different branch, or perhaps you’ve done more work since then and don’t want to reset --hard your current branch? You can use git cherry-pick. Let’s say the commit you want is i7j8k9l ("Refactor component Y").

$ git cherry-pick i7j8k9l

This command takes the changes introduced by the specified commit (i7j8k9l) and applies them as a new commit on your current branch. This is a safer way to recover specific commits without altering your current branch’s history drastically.

It’s important to remember that reflog entries are not permanent. By default, Git prunes (removes) entries in the reflog that are older than 90 days, or entries that point to commits that have been garbage collected (which happens if they haven’t been referenced for a while). So, while git reflog is a powerful recovery tool, it’s best used relatively soon after the "loss."

A common misconception is that git reflog is a way to undo public history. It’s not. Reflog entries are local to your repository. If you’ve already pushed a branch with the "lost" commits and then rewritten history locally to remove them, pushing again will overwrite the remote history. git reflog can help you recover locally, but you’ll need to be careful about how you re-publish that history.

When you git reset --hard to recover commits, and then you push, you will likely encounter a "rejected non-fast-forward" error because the remote branch has diverged from your new local history. This is where you’d typically use git push --force-with-lease to overwrite the remote history, but that’s a topic for another day.

After recovering your lost commits and pushing them, the next thing you might grapple with is managing merge conflicts if the recovered commits introduce changes that clash with the current state of your project.

Want structured learning?

Take the full Git course →