Rewriting History with Interactive Rebase
Real work is messy: "WIP" commits, typo fixes, a forgotten file. Before sharing a branch, you can clean it up with git rebase -i — combine commits, reword messages, reorder them, or drop ones you don't want. The result is a history that's easy to review and understand.
The messy branch
git log --oneline
0d5547f (HEAD -> main) Temporary debugging 92f1601 Show basket on home page c134279 fix total, oops 2773a93 WIP dbe9cea Add basket total function 271266e Create home page
Sam wants to: fold "WIP" and "fix total, oops" into the commit that added the function, drop the debugging commit, and reword the basket commit to be more precise.
Starting an interactive rebase
Tell Git how far back to go — here the last five commits, HEAD~5. Git opens your editor with a todo list, one line per commit, oldest first:
git rebase -i HEAD~5
pick dbe9cea Add basket total function pick 2773a93 WIP pick c134279 fix total, oops pick 92f1601 Show basket on home page pick 0d5547f Temporary debugging
Below the list, the file also contains comment lines (starting with #) summarising the available commands.
Each line starts with a command, followed by the commit's hash and message. Changing the commands and the order of the lines tells Git what to do:
| Command | Effect |
|---|---|
pick (p) | Keep the commit as it is |
reword (r) | Keep the changes, but edit the message |
edit (e) | Stop at this commit so you can change it (amend, split…) |
squash (s) | Combine with the previous commit and edit the combined message |
fixup (f) | Combine with the previous commit, discarding this commit's message |
drop (d), or delete the line | Remove the commit entirely |
| Move a line | Reorder commits |
Editing the plan
Sam changes the todo list to this, then saves and closes the editor:
pick dbe9cea Add basket total function fixup 2773a93 WIP fixup c134279 fix total, oops reword 92f1601 Show basket on home page drop 0d5547f Temporary debugging
Git then works through the list. For the reword line it opens the editor again with the old message; Sam changes it to "Show basket summary on home page":
git rebase -i HEAD~5
[detached HEAD 7f0c0d3] Show basket summary on home page Date: Mon Mar 2 09:49:00 2026 +0000 1 file changed, 1 insertion(+) Successfully rebased and updated refs/heads/main.
git log --oneline ls
7f0c0d3 (HEAD -> main) Show basket summary on home page e9128a5 Add basket total function 271266e Create home page basket.js index.html
Three commits instead of six, each one complete and clearly described, and the debugging file is gone. The final code in basket.js is exactly what it was before — only the history changed.
Fixup commits and autosquash
When you notice a mistake in an earlier commit while you're still working, you can mark the fix for later. git commit --fixup=<commit> creates a commit whose message starts with fixup! followed by the target's message:
echo "function total(items) { return items.reduce((sum, i) => sum + i.price * i.qty, 0); }" > basket.js
git commit -a --fixup=HEAD~1
git log --oneline
[main 9be0547] fixup! Add basket total function 1 file changed, 1 insertion(+), 1 deletion(-) 9be0547 (HEAD -> main) fixup! Add basket total function 7f0c0d3 Show basket summary on home page e9128a5 Add basket total function 271266e Create home page
Later, --autosquash moves each fixup commit next to its target in the todo list and marks it fixup automatically — you just save the list:
git rebase -i --autosquash HEAD~3 git log --oneline
Successfully rebased and updated refs/heads/main. a31396c (HEAD -> main) Show basket summary on home page 1c79c12 Add basket total function 271266e Create home page
Set git config --global rebase.autoSquash true to make this the default for every interactive rebase.
Splitting a commit
Mark a commit with edit. When the rebase stops there, undo the commit but keep its changes, then commit them in pieces:
git reset HEAD~1 # changes are now unstaged edits git add basket.js git commit -m "Add basket total function" git add index.html git commit -m "Show basket summary on home page" git rebase --continue
Rules and safety
- Same rule as any rebase: only rewrite commits that you haven't shared, or that are on your own feature branch.
- If something goes wrong mid-way,
git rebase --abortputs everything back. - If you notice a mistake afterwards, the reflog (lesson 20) still has the old commits:
git reset --hard ORIG_HEADright after the rebase, or find the entry withgit reflog. - To edit the very first commit, use
git rebase -i --root.
Try it yourself
Make five quick commits with sloppy messages. Use one interactive rebase to squash two of them, reword one, drop one and swap the order of the remaining two. Check the result with git log --oneline and git show.