Skip to content
GKgkml.dev
← Learn Git & GitHub

Lesson 6 of 7 · 9 min

Recovery and investigation

The reflog, bisect, blame and stash — how to find what broke and get back what you lost.

The reflog is the safety net

Every time HEAD moves — commit, checkout, reset, rebase, merge — Git appends the previous position to the reflog. A bad reset, a rebase that dropped a commit, a deleted branch: the old commit is still in the object database and the reflog still names it.

It is local and expires after about 90 days by default. It cannot recover work that was never committed, which is the one thing to remember.

Undoing a bad reset or a lost branch
git reflog                        # every position HEAD has held
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# 9f8e7d6 HEAD@{1}: commit: the work I just lost

git reset --hard HEAD@{1}         # back to before the reset
git branch recovered 9f8e7d6      # or give the commit a name

git fsck --lost-found             # for objects with no reflog entry

revert versus reset

revert creates a new commit that undoes an earlier one, leaving history intact. It is the correct way to undo anything already pushed, because it adds rather than rewrites.

Reverting a merge commit needs -m 1 to say which parent is the mainline. And once a merge is reverted, re-merging that branch later does nothing — the commits are already ancestors. You revert the revert.

Bisect, automated
git bisect start
git bisect bad HEAD          # broken here
git bisect good v2.3.0       # working here
git bisect run npm test -- --silent    # Git drives the search

# Exit 0 = good, 1-124 = bad, 125 = skip this commit.
git bisect reset             # back to where you started

Note: 1000 commits take about ten steps. Automating it with the failing test is what makes bisect worth reaching for.

Blame, and seeing past reformatting

git blame shows which commit last touched each line, but a formatting sweep makes every line look recently changed. Adding -w ignores whitespace, and --ignore-rev with a revs file lets you permanently skip known formatting commits.

git log -S 'someString' searches for commits that changed the number of occurrences of a string — the fastest way to find when a function was introduced or removed, and far more useful than scrolling the log.

Worth remembering

  • The reflog records every position HEAD held, so almost nothing is truly lost.
  • bisect finds the breaking commit in log₂(n) steps and can be fully automated.
  • revert is the safe undo for published history; reset is for local history.

Test it now

Recovery scenarios and history archaeology fill much of the hard bank.