Clean Up Messy Git Commits Before a Pull Request with Interactive Rebase
Use git rebase -i to squash, reword, and reorder local commits into a clean pull request before pushing.
30 Jun 2026, 01:55 UTC

The problem: a feature branch full of "wip" commits
You have been working on a login feature for two days. Your branch contains a dozen commits with messages like "fix typo", "wip", "try again", and "actually works now". The code is fine, but the history is noisy. Reviewers will have to dig through false starts to understand what changed.
You do not need to keep every intermediate checkpoint. Git lets you rewrite local history before you share it, so the branch tells a clean story: one commit for the form, one for validation, one for the API call.
Use interactive rebase to reshape local commits
Run git rebase -i HEAD~n in your local terminal on the feature branch, where n is the number of recent commits you want to review. Git opens a todo file listing those commits in reverse chronological order. You need write access to the local repository.
Each line starts with an action word. The most useful ones are:
pick— keep the commit as is.reword— keep the changes, edit the commit message.squash— fold the commit into the previous one and combine messages.fixup— fold the commit into the previous one and discard its message.drop— remove the commit entirely.
Change pick to squash or fixup for work-in-progress checkpoints, save the file, and close the editor. Git replays the commits and creates new commit hashes for anything it touched.
Worked example: collapse six checkpoints into two commits
Start on your feature branch:
git checkout feature/login
git rebase -i HEAD~6
Git opens an editor with something like this:
pick a1b2c3d Add login form
pick e4f5g6h wip
pick i6j7k8l fix typo
pick m9n0o1p Add validation
pick q2r3s4t try again
pick u5v6w7x Add API call
Edit it so the checkpoints fold into their related work:
pick a1b2c3d Add login form
fixup e4f5g6h wip
fixup i6j7k8l fix typo
pick m9n0o1p Add validation
fixup q2r3s4t try again
pick u5v6w7x Add API call
Save and close. Git replays the selected commits. If you used squash instead of fixup, Git opens a second editor to combine the commit messages; fixup skips that step and keeps the previous message.
Verify the result:
git log --oneline
An expected check is fewer commits in the output, and new hashes for rebased commits. Do not compare hashes to the remote branch yet; they have diverged intentionally.
Push the cleaned branch. Because the history changed, a normal push will be rejected. You need write permission to the remote branch. Use:
git push --force-with-lease origin feature/login
--force-with-lease refuses to overwrite the remote if someone else has pushed new commits since you last fetched. It is safer than git push --force, which can discard other people's work without warning.
Trade-offs and limits
Interactive rebase rewrites history, so never use it on commits that collaborators have already pulled. If another developer has based work on your old commits, rebasing forces them to reconcile divergent history.
Rebase can also pause for conflicts. Git stops at the conflicting commit and asks you to resolve it, then run git rebase --continue. You can abort at any time with git rebase --abort, which returns the branch to its state before the rebase started.
Finally, rebasing does not reduce repository size or delete unreachable objects immediately. Old commits remain in Git's object database until garbage collection runs.
If something goes wrong, use the reflog
Because rebase changes state, keep an escape route. Before you start, note the current commit:
git log --oneline -1
If the rebase leaves the branch in a bad state, run:
git reflog
Find the entry just before the rebase, then reset to it:
git reset --hard HEAD@{1}
Replace HEAD@{1} with the actual reflog reference from your output. This restores the pre-rebase branch tip.
Check before you merge
After pushing, open the pull request and confirm the commit list shows only the logical units you intended. Run git log --oneline origin/main..feature/login to see exactly which commits your branch adds over the base branch. If the history looks clean, request review. If not, rebase again locally before anyone pulls the branch.
Interactive rebase is a local hygiene tool, not a team coordination tool. Use it on your own branches, before sharing, and verify with git log before you force-push.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.