In a Git interactive rebase todo list, how do squash and fixup differ?
answer
- both fold into the line above
- the diff result is identical for both
- the difference is one editor prompt
- one keeps a message, one discards it
- squash asks you to merge messages
basics
~20 sBoth fold a commit into the one on the line above it. Squash opens an editor with both commit messages so you can write a combined one; fixup keeps the earlier commit's message and throws the folded commit's message away, with no editor prompt.
solid answer
~40 sContent-wise they are identical: `squash` and `fixup` both merge the marked commit's changes into the commit on the **previous** todo line, so two commits become one. The difference is the message. `squash` (`s`) concatenates both messages into an editor buffer and makes you produce the final message; `fixup` (`f`) silently discards the folded commit's message and keeps the earlier one, so the rebase runs without stopping. Use `squash` when the second commit adds something worth saying — "and handle the empty case" — and `fixup` when it is pure noise like "typo" or "address review comment". Neither can appear on the first line, since there is no earlier commit to fold into. Recent Git also offers `fixup -C`, which folds the changes but keeps the *later* commit's message instead.
code
text · 4 linespick 9f2a1c3 add rate limiter
fixup 4b7e0aa typo
squash c11d902 also limit the admin endpoints
pick 77aa310 add rate limiter docsgo deeper
Remember that both verbs combine a commit into the one above it, and that fixup is the quiet one that keeps the earlier message.
Explain that the trees are identical either way and only message handling differs, and know why neither verb is legal on the first line.
Show taste about which follow-up commits deserve a message in the final history, and check the squashed result with an empty diff against the old tip before force-pushing.
Own the team norm: what a reviewable commit looks like, whether cleanup happens before review or at merge time, and the cost of rewriting branches other people build on.
## The shared behaviour When `git rebase -i` replays the todo list, both `squash` and `fixup` mean "do not create a commit of your own; add these changes to the commit produced by the previous line". The result is one commit whose tree is the combination of the two, sitting where the earlier one was. You can chain them: three `fixup` lines under one `pick` collapse four commits into one. Because both fold **upward**, neither is legal on the first line of the todo list — Git refuses the list with an error saying the first command cannot squash, since there is nothing before it to squash into. If you want the *first* commit gone into the second, reorder the lines first. ## The difference: the commit message `squash` stops the rebase and opens your editor with a buffer containing both messages, commented with a header explaining that this is a combination of two commits. You delete, merge or rewrite them and save; what you save becomes the message of the combined commit. `fixup` does not stop at all. The folded commit's message is discarded and the earlier commit keeps its message verbatim. That is the whole point: a rebase of ten commits with six `fixup` lines runs to completion with no prompts. Recent Git added two options on the `fixup` line. `fixup -C <commit>` folds the change but takes the **later** commit's message as the message of the result — useful when the follow-up commit is the one that describes the finished change. `fixup -c` does the same but opens the editor so you can adjust it, which makes it behave like `squash` with the messages the other way round. ## Choosing between them A useful rule: does the folded commit's message tell a future reader anything? - "fix typo", "oops", "address review comment", "rerun formatter" — nothing of value. `fixup`. - "also handle the empty-input case", "add the missing index" — that is real information about what the final commit does. `squash`, then write a single message that covers both. A history full of "fix review comment" commits is exactly what reviewers and future archaeologists complain about, and `fixup` is the tool that removes them without you having to hand-write a merged message ten times. ## How this pairs with prepared fixup commits The todo verbs are one half of a workflow. The other half is creating commits that are already marked as belonging to an earlier one, so the todo list can be arranged automatically instead of by hand — that is what `--autosquash` consumes. Even so, the verbs stand on their own: you can always open the todo list and type `fixup` yourself, which is what most people do the first time. ## Things that go wrong - **Squashing into the wrong neighbour.** The verb folds into the line above *in the todo list*, not into the commit that looks related. If you also reorder lines, check what is now above. - **A conflict during a fold.** Folding replays two changes onto the same base; if the later one depended on intermediate state you dropped or moved, you will get a conflict and have to resolve it before continuing. - **Squashing published commits.** Folding rewrites every commit from that point on, so a pushed branch will diverge from its remote and need a force push. That is fine for a personal feature branch and a problem for a shared one. - **Expecting fixup to delete the change.** It discards the *message*, never the diff. Dropping a change entirely is `drop`. - **Blank squash message.** If you empty the editor buffer during a `squash`, the rebase stops with an error rather than silently committing an empty message. ## Verifying the outcome After the rebase, `git log --oneline` should show the folded commits gone, and `git diff` between the old tip (from the reflog) and the new tip should be empty when you only folded commits — the content is meant to be identical, only the packaging changed. That empty diff is the sanity check worth mentioning in an interview, because it proves the cleanup did not silently drop work.
- Why does Git reject a todo list whose first line is squash or fixup?Both verbs mean "fold into the commit produced by the previous line", and the first line has no previous line — the commit before it is the rebase base, which is not being rewritten. Git errors out immediately rather than guessing. The fix is to reorder so a `pick` comes first, or to start the rebase one commit earlier.
- You folded five commits into one and want to be sure nothing was lost. How do you check?Compare trees, not logs: `git diff <old-tip> <new-tip>` should be empty, since squashing changes packaging and not content. The old tip is available from `git reflog`. If the diff is non-empty, a change was dropped or a conflict was resolved wrongly during the replay.
- When would you use fixup -C instead of plain fixup?When the later commit carries the message you want to keep. Plain `fixup` always keeps the earlier commit's message; `fixup -C` folds the same changes but takes the later commit's message for the result, and `fixup -c` does that with an editor. It is handy when the follow-up commit is the one that finally describes the finished change.
saying these in an interview costs you the question
- Claiming fixup discards the commit's changes, not just its message
- Thinking squash merges into the commit below the line
- Believing squash and fixup produce different trees
- Putting squash on the first todo line and expecting it to work
- Assuming folding commits cannot cause conflicts