Git & GitHub · lesson 8 of 12
Rewriting History: Amend and Interactive Rebase
Fix a commit message and fold in a forgotten file with git commit --amend, then squash three sloppy WIP commits into one clean commit with interactive rebase.
By SkillByExample · Published
commit --amend rewrites the single most recent commit. Interactive rebase
(rebase -i) rewrites any range of recent commits — reordering, combining, or
dropping them. Both are safe on commits nobody else has pulled yet.
1) Fix a typo’d commit message
$ git commit -m "add cofnig"
$ git log --oneline -1
cdc8dd8 add cofnig
$ git commit --amend -m "Add config module"
$ git log --oneline -1
82464c3 Add config module
--amend doesn’t edit the old commit in place — it creates a brand-new commit with the
corrected message and the same parent, then moves main to point at it. cdc8dd8 no
longer exists on any branch.
2) Fold a forgotten change into the same commit
$ # ...realize config.py is missing a line, edit it...
$ git add config.py
$ git commit --amend --no-edit
$ git show --stat HEAD
[main 069e689] Add config module
1 file changed, 2 insertions(+)
create mode 100644 config.py
--no-edit keeps the existing message and just replaces the snapshot with whatever is
now staged — the missing line joins the same commit instead of becoming its own.
3) Three sloppy commits, squashed into one
$ git log --oneline -4
cb34b76 WIP finish feature
0b0c40d WIP more
bb6ae01 WIP
069e689 Add config module
An interactive rebase against the commit three back (HEAD~3) opens an editor with a
todo list like:
pick bb6ae01 WIP
pick 0b0c40d WIP more
pick cb34b76 WIP finish feature
Changing the last two lines’ pick to squash tells Git to fold each one into the
commit above it, in order:
pick bb6ae01 WIP
squash 0b0c40d WIP more
squash cb34b76 WIP finish feature
4) The result: one commit, all three diffs combined
$ git log --oneline -2
26b7ef1 WIP
069e689 Add config module
Three commits became one — but Git kept the first commit’s message (WIP) as the
default for the combined result, since that line was still pick, not squash.
scratch.py’s content is now the union of all three WIP edits.
5) Rename it with amend, the same tool from step 2
$ git commit --amend -m "Add scratch calculation feature"
$ git log --oneline -2
73f7789 Add scratch calculation feature
069e689 Add config module
The squash produced 26b7ef1; amending it for a real message produced 73f7789 — a
second new hash, same content, better message. This two-step shape (squash first,
rename after) is exactly what the reflog shows in the next lesson.
Practice
-
Run
git commit --amendwith no-mflag at all and watch it open your editor pre-filled with the old message — confirm cancelling the edit (saving unchanged) still keeps the amend (new hash, same text). -
In an interactive rebase todo list, change a
picktodrop(or just delete the line) for a commit in the middle of the range. Confirm that commit’s changes disappear from history entirely, not just its message.Solution
Dropping a middle commit still replays the commits above it, but without that commit’s diff — if a later commit depended on it, you’ll get a conflict during the rebase, exactly like a merge conflict.
-
Use
rewordinstead ofpickon one line of a todo list to change only that commit’s message, leaving its content and every other commit untouched. -
Reorder two
picklines in a todo list (swap their order) and confirm Git replays them in the new order — watch for a conflict if the second commit (now first) doesn’t build on the first’s changes. -
Run
git reflogimmediately after a squash and find the pre-rebase commits (bb6ae01,0b0c40d,cb34b76) still present — rebase doesn’t delete objects immediately, it just stops pointing branches at them.