Learning Objectives
By the end of this lesson you will be able to:
- Collapse several work-in-progress commits into one meaningful commit
- Choose correctly between
squashandfixup - Explain which commit absorbs which, and in which direction
- Use
git commit --fixupto mark a commit for automatic collapsing
The Branch Everyone Recognises
git log --oneline main..HEAD
7b2e551 fix typo
4c1f8ab actually fix it
9d3e072 fix
1a77c60 add refund boundary tests
Four commits, one idea. The last three are not decisions, they are keystrokes. A reviewer reading them learns nothing, and a future git bisect gets three commits where two of them do not build.
What you want is one commit that says: add refund boundary tests.
Squashing Them
git rebase -i main
pick 1a77c60 add refund boundary tests
squash 9d3e072 fix
squash 4c1f8ab actually fix it
squash 7b2e551 fix typo
The rule in one sentence: squash merges this commit into the line above it.
Above is older. So the three squash lines all fold into 1a77c60, and you end up with one commit containing all four sets of changes.
Save and close. Git combines them and opens an editor with all four messages so you can write the real one:
# This is a combination of 4 commits.
add refund boundary tests
fix
actually fix it
fix typo
Delete the noise, write the message you actually want, save:
test: add boundary cases for zero and negative refunds
Covers the three amounts the API rejects and the two it rounds.
Result:
git log --oneline main..HEAD
d4a91e0 test: add boundary cases for zero and negative refunds
One commit. One idea. Reviewable.
fixup: Same Thing, Without the Message Prompt
fixup does exactly what squash does, except it throws the squashed commit's message away and does not open an editor.
pick 1a77c60 add refund boundary tests
fixup 9d3e072 fix
fixup 4c1f8ab actually fix it
fixup 7b2e551 fix typo
The result is the same single commit, keeping only add refund boundary tests as its message, with no editor step at all.
Which to use:
| Use it when | |
|---|---|
squash |
The messages contain something worth keeping, or you want to write a new combined message |
fixup |
The messages are fix, wip, oops — pure noise |
In practice fixup is the one you reach for most, because most noise commits are named exactly like noise commits.
The Direction Trap
The commit at the top of a squash group must be a pick. Git has to have something to squash into.
squash 1a77c60 add refund boundary tests ← error: cannot squash the first commit
pick 9d3e072 fix
error: cannot 'squash' without a previous commit
If you meant to keep the newest message rather than the oldest, do not flip the commands — reorder the lines so the commit whose message you want is at the top of the group, then squash the rest into it. This is why interactive rebase lets you reorder at all.
git commit --fixup: Marking as You Go
There is a way to avoid ever writing fixup in the editor. When you are fixing something in a commit that is already on your branch:
git add tests/payments/refund.spec.ts
git commit --fixup 1a77c60
That creates a commit whose message is fixup! add refund boundary tests. It sits at the top of the branch, going nowhere, clearly labelled.
Then, when the branch is ready:
git rebase -i --autosquash main
--autosquash reads those labels, moves each fixup! commit up next to the commit it names, and pre-fills the command as fixup. The todo list arrives already correct — you check it and save.
Make it permanent:
git config --global rebase.autosquash true
Pro Tip:
git commit --fixup <sha>while you work, then a singlegit rebase -i mainbefore the PR, is the whole professional workflow. You never have to remember which commit a fix belonged to, because you recorded it at the moment you knew.
What This Buys You Later
A cleanly squashed branch is not cosmetics. In Module 6, git bisect steps through history one commit at a time, testing each. Commits that do not build are commits bisect has to skip, and a wip commit that only compiles once the next commit lands is exactly that. Squashing them means every commit in main is testable — which is what makes automated bisect possible at all.
Key Takeaways
squashandfixupboth merge a commit into the line above it — above means oldersquashcombines the messages and opens an editor;fixupdiscards the message silently- The first line of any squash group must be
pick - To keep a different message, reorder the lines rather than changing the commands
git commit --fixup <sha>labels a fix at the moment you make itgit rebase -i --autosquashthen arranges the todo list for you automatically- Squashed, buildable commits are what makes
git bisectreliable later