Cleaning Up Your Git History with Interactive Rebase
Stop cluttering your project history with 'fix typo' commits. Learn how to use Git interactive rebase to squash and reword commits, maintaining a clean, linear project history before merging.
14 Jul 2025, 21:46 UTC

The Problem: The 'Work-in-Progress' Noise
\nMost developers commit frequently. You might have commits titled \"save point,\" \"fixing typo,\" or \"trying one more thing\" scattered across a feature branch. While these are helpful during active development, they create noise in the project history. When a teammate reviews your Pull Request or a lead looks back at the logs to find where a bug was introduced, these micro-commits obscure the actual logic of the change.
\nThe goal is to present a clean, linear history where each commit represents a single, logical unit of work. Git's rebase -i (interactive rebase) allows you to rewrite your local history to achieve this before your code ever hits the main branch.
How Interactive Rebase Works
\nInteractive rebasing is essentially a way to tell Git: \"I want to go back to a specific point in time and rewrite everything that happened after it.\" When you trigger an interactive rebase, Git opens a temporary text file (the TODO list) in your configured editor. This file lists every commit between your current state and the target commit.
\nEach commit is prefixed with a command. The default is pick, which tells Git to keep the commit as is. However, you can change pick to several other actions:
- \n
- Reword: Keeps the commit's changes but lets you edit the commit message. \n
- Squash: Melds the commit into the previous one and prompts you to combine the messages. \n
- Fixup: Like squash, but it discards the commit message of the current commit, keeping only the previous one. \n
- Drop: Completely removes the commit from the history. \n
Worked Example: Consolidating a Feature Branch
\nAssume you are on a feature branch and have three commits that should actually be one clean feature implementation. You can run this command from your terminal (requires Git 2.x+):
\n\n# Rebase the last 3 commits on the current branch\n git rebase -i HEAD~3\n\nYour editor will open a file looking like this:
\npick a1b2c3d Initial feature implementation\npick e4f5g6h Fix typo in variable name\npick i7j8k9l Add missing unit test for edge case\n\nTo consolidate these into one commit, change the actions to fixup (or f) for the noise commits:
pick a1b2c3d Initial feature implementation\nfixup e4f5g6h Fix typo in variable name\nfixup i7j8k9l Add missing unit test for edge case\n\nSave and close the editor. Git will now apply the changes from the second and third commits directly into the first. To verify the result, run:
\ngit log --oneline\n\nYou should now see only one commit instead of three, with the history reflecting the final state of the code rather than the trial-and-error process.
\n\nThe Critical Trade-off: Public vs. Private History
\nInteractive rebasing changes the commit hashes. Because Git identifies commits by their SHA-1 hash, rewriting a commit creates a brand new object. This leads to a significant risk: never rebase commits that have already been pushed to a shared public branch.
\nIf you rewrite history that others have already pulled, their local history will diverge from the remote. When they try to pull again, Git will attempt to merge the two versions of the same changes, resulting in a \"duplicate commit\" nightmare and complex merge conflicts.
\nIf you must update a remote feature branch after a rebase, avoid git push --force. Instead, use:
git push --force-with-lease\nThis command checks if the remote branch has been updated by someone else since your last fetch. If it has, the push will fail, preventing you from accidentally overwriting a colleague's work.
\n\nFinal Checklist for a Clean History
\n- \n
- Local only: Only rebase commits that exist on your local machine or your private feature branch. \n
- Atomic commits: Use
squashandfixupto ensure each commit in the final history is a complete, working change. \n - Verify: Always run your test suite after a rebase to ensure that reordering or dropping commits didn't introduce a regression. \n
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.