DevOps
Part 4 of 6 · GitHistory Surgery — Amend, Interactive Rebase, Fixup & Autosquash
History surgery cleans private commits before review. Amend fixes the tip. Interactive rebase reorders, squashes, rewords, or edits. Fixup and autosquash fold review follow-ups into the commit they belong to. Every one of these tools mints new commit ids.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
Question ladder
L1
How do squash and fixup differ?
Answer
Both fold the changes into the previous commit. Squash opens the combined message. Fixup keeps the earlier message and drops this one.
L2
Why did amend change the SHA that CI built?
Answer
Amend wrote a new commit object. Anything pinned to the old id, including a pipeline, must move to the new id.
L3
What do pick, reword, edit, squash, fixup, and drop do?
Answer
Pick keeps the commit. Reword edits the message. Edit pauses so you can amend or split. Squash and fixup fold into the previous commit. Drop removes it.
L4
What does --autosquash change in the todo list?
Answer
It moves fixup! and squash! commits under the commit whose subject they name, so you do not reorder by hand.
L5
How do you split one commit during an interactive rebase?
Answer
Mark it edit. When Git stops, mixed-reset the tip, stage the first slice, commit, stage the rest, commit, then rebase --continue.
L6
What happens to a signature when you rewrite?
Answer
The old signature does not apply to the new object. Sign again with -S if the policy requires it.
L7
When do you refuse to operate and use revert instead?
Answer
When the commit is already on main, or when coworkers are on the same branch and have not agreed to a rewrite.
Failure modes
Amend of a published tip others pulled
Their branch still names the old commit. Your lease push does not update their machines.
Fixup into the wrong commit
Autosquash trusts the subject line. A sloppy fixup! message folds the patch into the wrong parent.
Dropping a commit later commits need
The replay builds until the dependent commit, then conflicts or loses the behavior.
Squashing unrelated concerns
The cleaned commit is no longer bisectable. One id now contains two bugs.
Forgetting rebase --continue after edit
You are still mid-rebase. The branch is not the history you think you pushed.
Misconceptions
Amend edits the commit object in place.
It writes a new object and moves the branch. The previous tip remains in the reflog.
Autosquash works with no marker in the message.
The message must start with fixup! or squash! plus the target subject, or you pass --fixup to commit.
Interactive rebase is safe on main if you are careful.
Careful still changes ids that other people and CI already stored. Use revert.
Interviewer traps
Listing squash and fixup as identical.
Say who keeps the commit message.
Rewriting a commit that is already on main to fix a typo.
A new commit or a revert. Surgery stays on the private branch.
Design scenario
Same prompt for every reader.
Requirements
Fold the typo into the logic commit. Keep the logic message. Push only the private branch. Do not rewrite main.
Traffic / scale
One author. One pull request. A few review cycles.
Latency
Cleanup happens before the next review, not after the squash lands on main.
Consistency
The remote feature tip moves only if the lease still matches. CI will see new commit ids.
Availability
rebase --abort returns to the pre-rebase tip if the todo list was wrong.
Failure assumptions
- The fixup subject does not match the target commit.
- The branch was already pushed, so the update cannot be a fast-forward.
Constraints
- Protected main. No force-push there.
- Use fixup rather than a noisy extra commit in the final story.
Prompt
A private branch has a scaffolding commit, the real logic, and a typo fix. Review asked for the typo to disappear into the logic commit before merge. main is protected.
API
Which rebase verb keeps the target message?
Data
Which commit ids change, and does the old tip remain in the reflog?
Architecture
Where does the rewritten feature branch sit relative to protected main?
Review found a typo in an earlier commit on your private branch
Prefer
fixup, then autosquash
The follow-up commit names its target. Interactive rebase folds it in and keeps the original message. The pull request story stays intentional.
- You do not hand-reorder the todo list.
- The target message stays, which is what fixup means.
- The push is a lease because the ids changed.
Alternative
Leave a chain of typo commits, or amend main
Noise commits hide the bug in review. Amending a commit already on main rewrites a shared id.
- Bisect and blame get harder before you even land.
- A force-push to main is the failure mode, not the cleanup.
- Revert is the shared-branch tool. It is the next page.
Replay a private todo list
Abort any time before you trust the new tip. The diagram is the same loop.
- 1
Start at origin/main
rebase -i origin/main, or HEAD~N when you know the count. - 2
Edit verbs
pick, squash, fixup, reword, edit, or drop. Autosquash may have ordered the fixups already. - 3
Resolve or continue
On a conflict, fix, add, and rebase --continue. Or abort and return to the old tip. - 4
Lease-push the new tip
CI will build the new ids. Protected main does not move.
Overview
Surgery is how a private branch becomes a reviewable story. It is the same rewrite as rebase. The verbs are just more specific. The safety rule does not change: if someone else has the commit id, do not replace it quietly.
Tools
| Tool | Scope | Keeps the target message? | Beats | Failure if wrong |
|---|---|---|---|---|
commit --amend | Tip only | Optional edit | A tiny extra fix commit | Amend a tip others pulled |
rebase -i squash | Several commits | You edit the combined message | A manual reset chain | Squashing unrelated work |
rebase -i fixup | Several commits | Yes | Squash, when the message is already good | Fixup into the wrong commit |
rebase -i reword | Several commits | You write a new message | Amend, which can only see the tip | Reword of shared commits |
rebase -i edit | Several commits | Pause to amend or split | A separate reset dance | Forgetting --continue |
rebase -i drop | Several commits | Not applicable | A private revert of noise | Dropping a commit others depend on |
| autosquash | Several commits | Via fixup! or squash! | Reordering the todo list by hand | A subject line that matches the wrong commit |
Amend
git add -p
git commit --amend --no-edit
git commit --amend
git push --force-with-lease origin HEADAmend changes the committer timestamp and the id. A signature does not carry over unless you sign again with -S. Do not amend a merge unless you are ready to reason about parents. Do not amend someone else's tip.
Todo verbs
git fetch origin
git rebase -i origin/mainA todo list starts like this:
pick a111 aaa WIP scaffolding
pick b222 bbb real logic
pick c333 ccc typo fixA cleaned list:
pick a111 aaa WIP scaffolding
squash b222 bbb real logic
fixup c333 ccc typo fix| Verb | Effect |
|---|---|
| pick | Keep the commit |
| reword | Keep the changes, edit the message |
| edit | Pause so you can amend or split |
| squash | Fold into the previous commit and edit the combined message |
| fixup | Fold into the previous commit and discard this message |
| drop | Remove the commit |
| break | Pause. Available on newer Git |
Decisions
- 1
1. Start rebase -i
- next2. Edit the todo list
- 2
2. Edit the todo list
- next3. Replay next commit
- 3
3. Replay next commit
- next4. Conflict?
- ?
4. Conflict?
- yes5. Fix, add, continue
- no6. Advance
- 5
5. Fix, add, continue
- next6. Advance
- 6
6. Advance
- next7. More commits?
- ?
7. More commits?
- yes3. Replay next commit
- no8. Tip updated
- 8
8. Tip updated
Lesson map
History Surgery — Amend, Interactive Rebase, Fixup & Autosquash
History surgery cleans private commits before review. Amend fixes the tip. Interactive rebase reorders, squashes, rewords, or edits. Fixup and autosquash fold review follow-ups into the commit they belong to. Every one of these tools mints new commit ids.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB start["1. Start rebase -i"] todo["2. Edit the todo list"] replay["3. Replay next commit"] hit["4. Conflict?"] start -->|1. Start rebase -i| todo todo -->|2. Edit the todo list| replay replay -->|3. Replay next commit| hit
git rebase --abort restores the pre-rebase tip. It is not a second path you have to draw. It is the cancel button while the replay is unfinished.
Fixup and autosquash
git commit --fixup=b222
git rebase -i --autosquash origin/maincommit --fixup writes a message fixup! plus the target subject. Autosquash moves that commit under the target in the todo list. You can also type the fixup! subject yourself.
Fixup beats another noise commit because review can stay iterative while the history you merge stays intentional. Squash is the verb when you still need to edit the combined message.
Scripted rebases often set GIT_SEQUENCE_EDITOR so the todo list is not opened by hand. The usual teaching form is still -i plus --autosquash.
Split a commit
git rebase -i HEAD~3
git reset HEAD~
git add -p
git commit -m "feat: part A"
git add -p
git commit -m "feat: part B"
git rebase --continueMark the target edit. The mixed reset (reset with no --hard) keeps the working tree and unstages the tip. You then make two commits and continue. This is still a private rewrite.
When not to operate
| Situation | Do this instead | Why |
|---|---|---|
| The commit is already on main | revert, or a forward fix | Other people and CI have the id |
| Coworkers are on the same branch | Coordinate, or merge | A force war is worse than a noisy commit |
| A regulated audit must keep the mistake | Additive commits | The rewrite hides the trail |
| The squash would mix unrelated work | Split before you merge | Bisect and review both need one concern |
Autosquash in a scratch repo
Illustrative only. Run it in an empty directory, not in a repo you share.
git init demo && cd demo
echo a > f && git add f && git commit -m "base"
echo b >> f && git commit -am "feature work"
echo c >> f && git commit -am "fixup! feature work"
GIT_SEQUENCE_EDITOR=: git rebase -i --autosquash HEAD~2
git log --onelineWhat the verb keeps
Press Run. Snippets must be self-contained — no network, files, or native modules.
Press Run. Snippets must be self-contained — no network, files, or native modules.
Interview Q&A
What is the difference between squash and fixup?
Answer
Both fold changes into the previous commit. Squash opens an editor for the combined message. Fixup keeps the previous message and discards the one on the folded commit.
Why did amend change the SHA CI already built?
Answer
The commit object is new. Any pin of the old id, including a workflow, a ticket, or a local branch a teammate has, still names the previous object.
Can interactive rebase reorder commits that will conflict?
Answer
Yes. The new order is the apply order, so conflicts can appear that a different order would have avoided. That is a reason to abort, not a reason to force the result.
Does --autosquash work without -i?
Answer
The reorder still needs the autosquash machinery. The common form is rebase -i --autosquash. Scripts often set GIT_SEQUENCE_EDITOR so the list is applied without a human editor.
What happens to signatures when you rewrite?
Answer
They do not transfer. The new object needs a new signature if policy says -S. The old signature still verifies the old object, which is no longer the branch tip.
How do you abort mid-rebase?
Answer
git rebase --abort restores the tip from before the rebase started, as long as you are still in that rebase.
Should you amend a merge commit?
Answer
You can, and it is easy to damage the parent list. Avoid it unless you are deliberately using lower-level tools and you can name both parents afterward.
What is a sane force-push policy?
Answer
Allowed with --force-with-lease on branches that are not protected. Prohibited on protected main. Announce it when anyone else might have pulled.
Pitfalls
fixup!text that matches the wrong subject, so autosquash folds into the wrong commit.- Editing a todo list and dropping the commit that introduced the function you later call.
- Amending with
--no-editwhen the message was the bug. - Assuming abort works after you have finished the rebase and pushed. At that point you want the reflog, which is the next page.
Write the todo list that squashes scaffolding into the logic commit and fixups the typo. Say which message survives, and which push flag you use if the branch is already on the remote.