Git for Engineers — Level by Level›06 · Undo and recovery

Lesson 06 of 8 · Level 2 — Working in a team

Undo and recovery

Almost nothing in Git is truly lost. Choose the right undo for the situation (restore, reset, revert), park work with stash, move commits with cherry-pick, find 'lost' commits with reflog, and find the commit that broke something with bisect.

Practitioner
Key wordsgit restoregit resetsoftmixedhardgit revertgit stashgit cherry-pickgit refloggit bisectlost commitsforce push

Pick the right undo

Situation Command Rewrites history?
Edited a file, want the last committed version back git restore <file> No
Staged something by mistake git restore --staged <file> No
Last commit is wrong, not pushed git commit --amend, or git reset --soft HEAD~1 Yes (local only)
Several local commits are wrong, not pushed git reset --soft/--mixed <sha> Yes (local only)
Commit is already pushed/shared git revert <sha> No: adds an undo commit
Want to match the remote exactly, discarding local work git reset --hard origin/main Yes, and discards changes

The three reset modes differ only in what happens to the changes from the removed commits:

Mode Changes end up Use
--soft Staged Re-commit them differently
--mixed (default) In the working tree, unstaged Re-stage selectively
--hard Gone from working tree Start clean (careful)

Undo in Git is like a notebook where you can cross out a line (revert: everyone can see it was crossed out), tear out a page you haven't shown anyone (reset), or look in the bin for a page you threw away this morning (reflog).

Stash: park work quickly

$ git stash push -m "wip: order search"     # clean working tree, work parked
$ git switch main && git pull               # deal with something urgent
$ git switch feature/search
$ git stash pop                             # bring the work back

Stashes are easy to forget; prefer a WIP commit on your branch for anything longer than a quick interruption.

Cherry-pick: copy a commit

$ git cherry-pick -x 7c1d2e3

Useful for hotfixes (lesson 05) and rescuing one good commit from an abandoned branch. For more than a couple of commits, a rebase or merge is usually cleaner.

Reflog: the safety net

Every time HEAD moves (commit, reset, rebase, checkout), the reflog records it locally:

$ git reflog -8
3f1c9a0 HEAD@{0}: reset: moving to HEAD~2
9e8d7c6 HEAD@{1}: commit: add order search tests
5b4a3f2 HEAD@{2}: commit: add order search
...
$ git branch rescue 9e8d7c6        # the "lost" commits are back on a branch

This is how you recover from a bad reset, a botched rebase, or a teammate's force push (if you had the commits locally). Protected branches (lesson 04) stop force pushes on shared branches in the first place.

Bisect: find the commit that broke it

$ git bisect start
$ git bisect bad                  # current commit is broken
$ git bisect good v1.3.0          # this release was fine
Bisecting: 57 revisions left to test after this (roughly 6 steps)
# test, then:  git bisect good   or   git bisect bad   ... until:
3f1c9a0 is the first bad commit
$ git bisect reset

Automate it with a script that exits 0 for good and non-zero for bad: git bisect run ./check.sh.

Secrets need more than an undo

If a secret was pushed, reverting or deleting the file doesn't help: it's still in history and in every clone. Rotate the secret first. Removing it from history (git filter-repo, or BFG) plus a force push is a separate, coordinated clean-up (see lesson 07).

Try it: break things and recover

  1. Make three commits, then git reset --hard HEAD~3. Recover all three with git reflog and git branch rescue <sha>.
  2. Push a commit to a test remote's main, then undo it with git revert and look at the history.
  3. Create 20 commits where one changes a test value to "broken"; find it with git bisect run and a one-line check script.
  4. Stash a change, switch branches, switch back and pop it.

Recap

  • Local, unpushed: restore / reset / amend. Shared: revert.
  • reset --soft keeps changes staged, --mixed unstaged, --hard discards them.
  • stash to park, cherry-pick -x to copy, reflog to find lost commits, bisect to find the breaking commit.
  • Leaked secrets: rotate first, then clean history deliberately.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.