How to rewrite and combine multiple local commits using interactive git rebase with squash
Learn how to organize your code history by combining messy commits into a single clean entry using Git interactive rebase. Discover how this practice improves project readability before sharing.
Summary
- Interactive rebase allows rewriting the local repository timeline before sharing changes with the team.
- The squash function merges the content of multiple smaller commits into a single cohesive, auditable delivery.
- Modifying history requires extreme caution after public push to avoid catastrophic conflicts with other developers.
- Resolving conflicts during the rebase process requires patience and specific commands to advance step by step.
- Maintaining standardized commit messages accelerates code reviews and simplifies future software security audits.
The importance of maintaining a clean Git history
During software development, it is common to create dozens of small local commits. In practice, this means saving progress with every minor tweak, fixing typos, or testing quick ideas. However, pushing this clutter to the main repository pollutes the project timeline. An organized history helps any engineer quickly understand the purpose of each modification months later.
When working in a team, code clarity also depends on how we narrate system evolution. Commits with generic or repetitive messages make tracking bugs in future audits much harder. This is precisely the scenario where interactive rebase becomes an indispensable tool in any developer's daily routine, enabling precise cleanup.
What interactive rebase is and how it works in practice
The git rebase command takes a series of changes and reapplies them on top of a new code base. In interactive mode, represented by the -i flag, Git opens a text editor displaying a list of your recent local commits. In practice, this means you gain total control over the order, content, and structure of these changes before they become permanent.
Imagine you wrote five consecutive commits to fix a single interface button. The first created the file, the second adjusted the color, the third fixed a typo, the fourth changed spacing, and the fifth finished it. To anyone reviewing the code, the first four steps are pure noise. Interactive rebase serves specifically to merge all of this into a single cohesive, clean commit, telling an understandable story.
Step-by-step guide to applying squash and combining commits
To start the cleaning process in your terminal, the first step is to identify how many commits you want to review. In practice, you execute the command specifying how many steps back you want to go in your local timeline.
git rebase -i HEAD~5The command above opens a file in your default editor listing the last five commits, from oldest to newest. Each line starts with the word pick, which means keeping the commit unchanged. To merge lower commits into the first commit of the list, change the word pick to squash, or simply s, on the corresponding lines you wish to absorb.
After saving and closing the configuration file, Git opens a new window asking you to edit the final message of the unified commit. In practice, you erase old messages and write clear, descriptive text summarizing all the work done in that block of changes, ensuring useful context for the rest of the team.
Essential precautions when rewriting code history
Rewriting history alters the mathematical identity of commits, changing hashes and temporal references. In practice, this means that if you have already pushed these commits to a shared remote server, rebase will create a severe conflict with the version your peers hold. The golden rule in software engineering is never to rebase commits that have already been published and shared.
If you have already pushed code to the remote repository and need to fix history, forcing updates with the --force flag becomes necessary. This practice should be avoided on main branches like main or master, remaining acceptable only on individual branches where no other developer is collaborating at the moment.
Handling conflicts during the unification process
When merging older changes, Git often encounters discrepancies that are hard to reconcile automatically. In practice, this results in a code conflict that temporarily halts the rebase process until you make a manual decision. The terminal will indicate which files were affected and where the points of disagreement lie.
To resolve the situation, open the flagged files, edit the code to keep the correct version, stage the corrected changes, and continue the process. The command you use to resume the workflow after solving issues is simple and direct in the terminal.
git rebase --continueIf the situation gets too complex and you want to discard all changes, Git provides an immediate safety net. At any moment before completing the process, you can abort the operation and return exactly to the initial state you were in before typing the rebase command.
git rebase --abortFinal considerations on organization and best practices
Mastering interactive rebase with squash elevates any developer's technical level, turning a chaotic workflow into a clean and professional history. The ability to tell the right story through code eases reviews, speeds up failure identification, and improves teamwork. Adopting this habit daily guarantees safer releases and long-term maintainability for any software project.
Repository organization is not just an aesthetic matter, but a fundamental pillar of software engineering health. By mastering history manipulation tools responsibly, you protect your code against unnecessary noise and build solid foundations for the continuous evolution of the systems you help build.