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.
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
- Make three commits, then
git reset --hard HEAD~3. Recover all three withgit reflogandgit branch rescue <sha>. - Push a commit to a test remote's
main, then undo it withgit revertand look at the history. - Create 20 commits where one changes a test value to "broken"; find it with
git bisect runand a one-line check script. - Stash a change, switch branches, switch back and pop it.
Recap
- Local, unpushed: restore / reset / amend. Shared: revert.
reset --softkeeps changes staged,--mixedunstaged,--harddiscards 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.