5. rebase, stash, revert, reset — when to use each¶
At a glance
Time: 2 periods · Tiers: 0 (cards), 1–3 · You need: a throwaway practice repo (never your real project), commit cards and sticky notes
Before this: Grade 8 Module 5 (restore, revert), Grade 9 Modules 3–4, Module 4.
By the end you can: park unfinished work, replay a branch on a newer main, tidy unpushed commits, choose the safe undo for shared history, and rescue commits you thought were lost.
Hook (5 min)¶
Your PR is behind main, a reviewer asked for a change, you have half-done work in the folder, and your last commit message says typo fxi. Which tools fix this safely — and which could destroy a teammate's work?
Understand it¶
The golden rule¶
Never rewrite history that other people already have.
Some tools add to history (safe anywhere). Others rewrite it — they replace commits with new ones. Rewriting is fine on commits that exist only on your computer; on shared, pushed branches it causes confusion and lost work.
| Tool | Adds or rewrites? | Use on pushed/shared commits? |
|---|---|---|
git revert |
adds a new commit | ✓ yes — the safe undo |
git stash |
doesn't touch history | ✓ |
git commit --amend |
rewrites the last commit | only if not pushed |
git reset |
moves the branch label | only if not pushed |
git rebase |
replays commits as new ones | only on your own branch (see below) |
stash — a drawer for unfinished work¶
You are halfway through a change and must switch branches (a reviewer needs a quick fix). Commit half-done work? No — stash it:
git stash # put uncommitted changes in a drawer; folder is clean again
git switch other-branch ... (do the urgent thing) ... git switch back
git stash pop # take the changes out of the drawer
git stash list # see what is in the drawer
Use the drawer for minutes or hours, not weeks — stashes are easy to forget.
revert — undo by adding (Grade 8 revisited)¶
Creates a new commit that does the opposite of an old one. History stays true and nobody's copy breaks. The right undo for anything already pushed.
reset — move the label¶
git reset moves the current branch label to another commit. Three strengths:
| Command | Branch label | Staging area | Working files | Use |
|---|---|---|---|---|
git reset --soft HEAD~1 |
moves back 1 | keeps the changes staged | unchanged | redo the last commit (e.g. combine commits) |
git reset HEAD~1 (mixed) |
moves back 1 | unstages | unchanged | un-commit, keep edits |
git reset --hard HEAD~1 |
moves back 1 | reset | reset — edits lost! | throw away commits and edits |
before: c1 -- c2 -- c3 -- c4 [main]
after reset HEAD~2: c1 -- c2 [main]
(c3, c4 still exist, unlabelled; reflog remembers them)
--hard is the most dangerous everyday command in Git. Check git status and git diff first.
Small sibling: git commit --amend replaces the last commit (fix its message or add a forgotten file). Only before pushing.
rebase — replay your work on a new base¶
Your branch started from an old main; main has moved. In Grade 10 you merged main into your branch. Rebase is the other option: it takes your branch's commits and replays them one by one on top of the new main, as if you had started today:
before: m1 -- m2 [main]
\
B1 -- B2 [feature]
after git rebase main:
m1 -- m2 [main] -- B1' -- B2' [feature]
B1' and B2' are new commits (new ids) with the same changes. Result: a straight, easy-to-read history and no merge commit. Price: history was rewritten. So:
- ✓ rebase your own feature branch that nobody else has built on — even if you pushed it for a PR (then push with
git push --force-with-lease, which refuses if someone else pushed in the meantime); - ✗ never rebase
mainor any branch others work on.
If a replayed commit conflicts, Git stops: resolve like a merge, git add, then git rebase --continue (or git rebase --abort to give up).
Merge or rebase? Both are fine; teams choose. Merge keeps history exactly as it happened; rebase makes it tidier. Follow the project's CONTRIBUTING rules.
reflog — Git's safety net¶
Commits "lost" by reset or rebase are not deleted at once. git reflog lists every place HEAD has been recently (usually kept 90 days). Find the id and bring it back:
git reflog # find the commit before the accident
git reset --hard <that-id> # or: git branch rescue <that-id>
Reflog is local — it is your computer's diary, not shared.
Which tool when?¶
| Situation | Tool |
|---|---|
| Interrupted with half-done work | stash |
| Bad commit already pushed / shared | revert |
| Typo in the last commit message, not pushed | commit --amend |
| Several messy local commits → one clean commit | reset --soft HEAD~N then commit (or interactive rebase) |
My feature branch is behind main |
merge main or rebase main (own branch only) |
| "I reset and lost my commits!" | reflog |
See it¶
Real output from a practice repo:
$ git status --short
M b.txt <- half-done work
$ git stash
Saved working directory and index state WIP on feature: 4992393 Extend b
$ git log --oneline --graph main feature
* 4992393 (feature) Extend b
* 24ff680 Add b
| * 547b291 (main) Add m on main
|/
* 5ab9aa8 Add a
$ git rebase main
Successfully rebased and updated refs/heads/feature.
$ git log --oneline --graph main feature
* 3ca532b (HEAD -> feature) Extend b <- new ids: these are new commits
* 181e7ad Add b
* 547b291 (main) Add m on main
* 5ab9aa8 Add a
$ git stash pop <- half-done work is back
$ git reset --hard HEAD~1 <- oops
HEAD is now at 181e7ad Add b
$ git reflog -3
181e7ad HEAD@{0}: reset: moving to HEAD~1
157ac83 HEAD@{1}: commit: Add b.txt with three lines
181e7ad HEAD@{2}: reset: moving to HEAD~3
$ git reset --hard 157ac83 <- rescued
Unplugged: the card table (20 min)¶
Lay the commit cards on the table with a main sticky note and a feature sticky note.
- revert: add a new card that undoes an old one. (Nothing moves.)
- reset: move the sticky note back two cards. Where do the skipped cards go? (They lie unlabelled — the reflog remembers them.)
- rebase: pick up the
featurecards, copy them onto new cards placed aftermain's newest card, move thefeaturenote; the old cards lie unlabelled. - stash: put the working sheet in a drawer.
Now give a second group a copy of the old feature cards before the rebase, and ask: what happens when they try to add their next card? (Their history no longer matches — that is why we never rebase shared branches.)
PRIMM lab (50 min)¶
Throwaway repo only: git init practice-undo, make 3 commits on main, branch feature with 2 commits, then 1 more commit on main.
- Predict what each command does, and write it down:
git stash,git rebase main,git reset --soft HEAD~1,git revert HEAD. - Run each in turn (stash with some uncommitted edits; then rebase
featureonmain; then reset; then revert). After each, rungit log --oneline --graph --alland compare with your prediction. - Investigate: compare commit ids before and after the rebase. Why are they different?
- Investigate:
git reset --hard HEAD~2, then usegit reflogto find and restore the lost commits. - Modify: squash your last 3 commits into one with a clean message (
git reset --soft HEAD~3 && git commit -m "…"). Then try the same withgit rebase -i HEAD~3(changepicktosquashfor the last two lines). - Make: write your own "which tool when" cheat sheet and keep it in your portfolio.
Parsons practice (5 min)¶
Match situation → tool: pushed bad commit → revert; unpushed typo commit → reset/amend; branch behind main → rebase or merge; interrupted → stash.
Common mistakes¶
| Symptom | Cause | Fix |
|---|---|---|
git push rejected after a rebase |
the remote has the old commits | on your own PR branch: git push --force-with-lease; on shared branches: don't rebase |
| Teammate's history "goes crazy" | someone rewrote a shared branch | agree a recovery with the teacher; protect main |
Edits gone after reset --hard |
--hard resets the working files |
committed work → reflog; uncommitted edits are truly lost |
| Forgot a stash for weeks | — | git stash list; git stash show -p stash@{0} |
| Stuck in the middle of a rebase | conflict | resolve, git add, git rebase --continue, or git rebase --abort |
| Interactive rebase opened Vim | — | edit, then :wq; or set VS Code as editor |
Practise more with free resources¶
| # | Exercise | Tier | Time | Hand in |
|---|---|---|---|---|
| 1 | Learn Git Branching: intro4 Rebase Introduction, rampup4 Reversing Changes, move1 Cherry-pick Intro, move2 Interactive Rebase Intro. |
1 / 3 | 40 min | levels done |
| 2 | Pro Git §7.7 Reset Demystified — the "three trees" explanation. Redraw its main figure with our three places from Grade 8. | 3 | 30 min | drawing |
| 3 | Pro Git §3.6 Rebasing — read "The Perils of Rebasing". Explain the golden rule in your own words. | 3 | 20 min | explanation |
| 4 | Pro Git §7.3 Stashing and Cleaning: try git stash -u with an untracked file. |
1 / 3 | 15 min | what -u does |
| 5 | Dangit, Git!? — read three rescues; which ones use reflog, reset or amend? | 3 | 15 min | table |
Exit ticket (5 min)¶
- Why is
resetrisky on a pushed branch? - What does
git reflogdo? - Misconception: "Rebase and merge produce identical history."
Key points¶
- Golden rule: never rewrite history others already have.
stashparks work ·revertundoes by adding ·resetmoves the label ·rebasereplays commits on a new base ·amendfixes the last commit.--hardthrows away working edits — check first.reflogrecovers commits lost by reset or rebase.- Merge vs rebase: both valid; follow the project's rules.
New words¶
stash, reset (soft/mixed/hard), amend, rebase, interactive rebase, squash, reflog, force-with-lease, rewrite history — see the glossary.
Extension quest¶
Use git cherry-pick <id> to copy one commit from another branch onto yours. When would that be better than merging the whole branch?
Teacher notes¶
- Timing: P1: hook 5, understand 20, card table 15. P2: see it 5, lab 30, Parsons + exit 5.
- Run this lesson on throwaway repos only. Do not allow
push --forceon shared branches; protectmainon the class server (Forgejo/GitHub branch protection: block force pushes). - Key misconception: "reset deletes commits." It moves a label; commits remain reachable via reflog until garbage-collected.
- Fallback Tier 0: the card table plus the second-group experiment is the whole lesson.
- Answer key: Exit 1: others already have those commits; moving the label (and force-pushing) breaks their history and can lose their work. 2: lists where HEAD has been, so you can find and restore "lost" commits. 3: False — rebase creates new commits in a straight line; merge keeps both lines and adds a merge commit. Lab 3: rebased commits are new objects with new parents, so new ids.