SeriesPart 3 of 8 // Git Quick Hits
gitWriting
Aug 24, 2026
3 min read
git-quick-hits

The Undo Button Nobody Uses: `git reflog`

An interactive rebase drops a commit you actually needed, and git log no longer shows it. It isn't gone — git reflog remembers every HEAD you've ever had, including the one from thirty seconds ago.

A ledger with a torn-out page floating just above the binding, still tethered by a thread, next to the ledger's own footnote listing every page ever removed.

You're cleaning up a hotfix branch before opening the PR. An interactive rebase seems like the right tool — squash a couple of commits, tidy the messages. You edit the todo list, save, and git log comes back one commit short. The regression test you added is nowhere to be found. git log, git status, git show — all clean, all show no trace of it.

It isn't gone. Git doesn't delete commits just because no branch points at them anymore. It stops displaying them, which feels the same in a moment of panic but is a very different problem to have.

What actually happened

A hotfix branch, three commits deep:

git log --oneline
# 981ffe8 log timeout duration for observability
# dc86bc5 add regression test for payment timeout
# 8552d69 guard against nil pointer in payment retry
# bdd86dc base commit

An interactive rebase to tidy things up drops the middle commit by mistake — easy to do when you're rewording lines in an editor and delete the wrong one:

git rebase -i bdd86dc
git log --oneline
# c7912b9 log timeout duration for observability
# 8552d69 guard against nil pointer in payment retry
# bdd86dc base commit

dc86bc5, the regression test commit, has vanished from history. The branch now has two commits where it used to have three, and there's no menu, no trash can, no obvious way to get it back through the commands you'd normally reach for.

Ask git what it actually did

Every time HEAD moves — a commit, a checkout, a rebase step, a reset — git records it in the reflog. It's a private log of your own movements through the repository, kept for 90 days by default and never pushed anywhere.

git reflog
c7912b9 HEAD@{0}: rebase (finish): returning to refs/heads/hotfix/payment-timeout
c7912b9 HEAD@{1}: rebase (pick): log timeout duration for observability
8552d69 HEAD@{2}: rebase (start): checkout bdd86dc
981ffe8 HEAD@{3}: commit: log timeout duration for observability
dc86bc5 HEAD@{4}: commit: add regression test for payment timeout
8552d69 HEAD@{5}: commit: guard against nil pointer in payment retry
bdd86dc HEAD@{6}: checkout: moving from main to hotfix/payment-timeout
bdd86dc HEAD@{7}: commit (initial): base commit

Right there at HEAD@{4}: dc86bc5, "add regression test for payment timeout." The commit object was never deleted — only the branch pointer that used to lead to it moved away during the rebase.

Bringing it back

The commit still exists, so you can cherry-pick it straight back onto the branch:

git cherry-pick dc86bc5
[hotfix/payment-timeout d933c82] add regression test for payment timeout
 1 file changed, 1 insertion(+)
 create mode 100644 regression_test.txt
git log --oneline
# d933c82 add regression test for payment timeout
# c7912b9 log timeout duration for observability
# 8552d69 guard against nil pointer in payment retry
# bdd86dc base commit

The regression test is back, with its original message and content intact. It landed on top rather than in its original position — that's fine for a hotfix branch you're about to squash and merge anyway; if you need the exact original order, git rebase -i again to move it into place.

When this saves you

Reflog is the answer to almost every "I think I just destroyed my work" moment:

  • A bad git reset --hard. The commits you had before the reset are still reachable from the reflog entry right before it.
  • An amend that overwrote the wrong commit. The pre-amend commit is sitting one entry back.
  • A rebase or merge you aborted halfway through and now can't remember the starting point of. git reflog shows exactly where HEAD was before you started.

The one thing reflog can't rescue is a commit that's aged out of it (90 days is the default gc.reflogExpire) and has since been garbage collected — or one that was never committed at all, just edited in your working tree and overwritten. Reflog tracks commits, not uncommitted changes.

Next time git log looks wrong after a rebase, don't assume the work is gone. Run git reflog first — it almost always remembers what you did five minutes ago, even when you don't.

Series contents

01
Automating Regression Hunts with `git bisect run`
Read
02
Parallel Branches Without Stashing: `git worktree`
Read
03
The Undo Button Nobody Uses: `git reflog`
Current
04
`git rerere`: Stop Resolving the Same Conflict Twice
Coming soon // Aug 31, 2026
05
`.gitignore` vs `.git/info/exclude` vs Global Excludes
Coming soon // Sep 7, 2026
06
Shipping a Repo as One File with `git bundle`
Coming soon // Sep 14, 2026
07
Attaching Build Metadata with `git notes`
Coming soon // Sep 21, 2026
08
Why Shallow Clones Break Your Changelog
Coming soon // Sep 28, 2026