Sooner or later you will need to undo something, park unfinished work, or move a single commit to another branch. These four commands cover almost every "Git emergency".
Learning objectives
- Stash and restore unfinished work.
- Choose between
reset,revertandrestore. - Move a commit with
cherry-pick. - Recover "lost" commits with
reflog.
1. stash
git stash push -m "wip: course filters"
git stash list
git stash pop # apply and drop
git stash apply # apply and keep
git stash drop stash@{0}
Use it to switch branches without committing half-finished work. Stashes are local and easy to forget — do not use them as long-term storage.
git stash push -u # include untracked files
git stash show -p stash@{0}
2. reset — move the branch pointer
git reset --soft HEAD~1 # undo the commit, keep changes staged
git reset --mixed HEAD~1 # undo the commit, keep changes unstaged (default)
git reset --hard HEAD~1 # undo the commit and discard the changes
--hard deletes uncommitted work. Check git status and git stash first if there is anything you might want.
On a shared branch, never reset. Use revert.
3. revert — undo with a new commit
git revert a1b2c3d
Creates a commit that reverses the change. History is preserved, so it is safe on main and in production. Reverting a merge commit needs a mainline parent:
git revert -m 1 <merge-commit>
4. restore — files, not history
git restore src/app.js # discard working-tree changes
git restore --staged src/app.js # unstage, keep the edits
git restore --source=HEAD~2 src/app.js
restore is the modern replacement for checkout -- <file>.
5. cherry-pick
Copy a commit from one branch onto another:
git switch main
git cherry-pick 7f3a9c2
Useful for a hotfix that landed on a release branch and is also needed on main. Resolve conflicts the same way as a merge, then git cherry-pick --continue.
Do not cherry-pick entire features — the duplicated commits diverge and future merges become confusing.
6. reflog — the safety net
Every movement of HEAD is recorded locally:
git reflog
# 7f3a9c2 HEAD@{0}: reset: moving to HEAD~1
# 1d2e4f6 HEAD@{1}: commit: Add filters
git reset --hard 1d2e4f6
A commit removed by reset --hard is still reachable through the reflog for about 90 days. This is why "I lost my work" is usually recoverable.
7. bisect — find the breaking commit
git bisect start
git bisect bad
git bisect good v1.2.0
# test, then:
git bisect good # or: git bisect bad
git bisect reset
Binary search over history. With a scripted test, git bisect run npm test finds the culprit automatically.
8. Choosing the right undo
| Situation | Command |
|---|---|
| Discard local file edits | git restore <file> |
| Unstage a file | git restore --staged <file> |
| Undo last local commit, keep work | git reset --soft HEAD~1 |
| Undo a pushed commit | git revert <sha> |
| Move one commit to another branch | git cherry-pick <sha> |
| Recover after a bad reset | git reflog + git reset --hard |
| Find the commit that broke a test | git bisect |
Common mistakes
reset --hardwith uncommitted work and no stash.- Resetting a branch others have pulled.
- Cherry-picking a chain of dependent commits one by one instead of merging.
- Forgetting that
reflogis local and expires.
Practice
Complete the recovery drills in the assignment. Break a scratch repository on purpose, then fix it four different ways.