08 Aug, 2025
5 min read
I was pair-programming with a colleague at work when he ran into a GitHub issue.
Let’s be honest, we all run into Git issues. Probably more often than we’d like.
This time, it was because he accidentally pushed a bunch of commits to a branch that didn’t belong there. Some were experimental, some were meant for a different feature - classic “oops” moment.
To fix it, he used git squash to combine all the commits into one. Then he did a git reset HEAD, carefully picked the changes he actually wanted, re-committed them, and pushed the branch again. Boom. Problem solved. Clean history, no drama.
And it only worked that smoothly because he had a solid grasp of Git.
Now, without that understanding, the alternative would’ve been messy, like multiple git reset HEAD attempts, undoing and redoing things blindly, or even blowing away the branch.
So in this article, I’ll walk through:
What git squash really is
When you should use it (and when you shouldn’t)
How it can save your neck
And how to do it safely without wrecking your repo
Let’s dive in.
First of all, there's no actual Git command called git squash. It’s not like git status
or git pull
.
When people say “git squash,” they’re usually talking about using:
bash
git rebase -i
…which allows you to squash multiple commits into one during an interactive rebase.
Just like in regular English, if you squash a bunch of things, you’re flattening them into one. That’s the whole idea here. Git squash lets you take multiple commits (maybe messy, experimental, or irrelevant ones), and combine them into a single, clean commit.
Once squashed, that commit becomes your new history. You can then reword it, push it, or cherry-pick it somewhere else. You're in full control.
Now, you might be wondering: “Why would I even need this?”
Let’s be real, this isn’t part of the usual day-to-day Git routine like:
bash
git status
git add .
git commit -m "fix: something"
git push
You can totally survive as a developer without ever touching git rebase or git squash.
But here's the thing. Knowing how to squash commits (and when to do it) puts you in a different league. It shows that you care about clean commit history, readable PRs, and being a thoughtful collaborator.
So… when should you squash?
Before merging a feature branch:
💡 Pro tip: Use git rebase -i HEAD~3
to interactively squash the last 3 commits.
When contributing to open source :
Maintainers love clean PRs. Many even request: “Please squash your commits before merging.”
To reword or polish commit messages:
git rebase -i lets you squash and also edit commit messages along the way. It’s your chance to make history readable.
Here's a real life example of how to squash commits. This example is from one of my active projects where I had to clean up my commit history before merging a feature branch.
The image above shows the commit history of my feature branch feat/map. Notice how the first four commits are all related to the same feature, but they have different messages and some are just WIP (work in progress) commits. You can agree with me that it makes more sense to make them into a single, meaningful commit.
To squash these commits, I ran:
bash
git rebase -i HEAD~4
This command opens an interactive rebase session for the last 4 commits.
To be able to edit the editor, if it is a vim editor, you can press i
to enter insert mode.
Next, I changed the last 3 commits from 'pick' to 'squash' while leaving the first one as 'pick' This tells Git to squash those commits into the first one.
after editing, you can save and exit the editor. In vim, you can do this by pressing Esc
, then typing :wq
which means write and quit,
and hitting Enter
to save the changes.
Git will then squash those commits into one, and you’ll be prompted to edit the commit message. You can write a new, meaningful message that summarizes the changes.
You can entirely delete all the text in the editor shown in the image above, and write a new commit message that summarizes the changes made in the squashed commits. Also, commented lines (lines starting with #
) will be ignored by Git, so you can leave them as is or remove them if you want.
In my case, I wrote a new commit message: "feat: set up mapping the british museum and UoN", and then left the rest of the commented lines as they were.
After saving and exiting the editor, Git will complete the rebase and squash those commits into one. You can then push your changes to the remote repository.
Before pushing, it is safe to run:
bash
git log --oneline --graph
This command shows you a visual representation of your commit history, so you can confirm that the squashing worked as expected.
you can see that the last 4 commits have been squashed into one commit with the message "feat: set up mapping the british museum and UoN". The commit history is now cleaner and more meaningful.
Upon confirming that the squashing worked as expected, you can push your changes to the remote repository using:
bash
git push --force-with-lease
This command force pushes your changes while ensuring that you don’t accidentally overwrite any changes made by others. it will refuse to push if the remote branch has new commits you don’t have locally. It is a safer way to push after a rebase.
After the push, you can then confirm on Github that the squash was successful by checking the commit history of the branch. You should see that the last 4 commits have been squashed into one commit with the message you provided.
How cool is that? You’ve just cleaned up your commit history like a pro! 😎
I started this article with a story about my colleague who used git squash to fix a messy commit history. That’s the power of knowing how to squash commits. Here are a few more real-world situations where this command can be your superhero cape:
The “Commit Spam” Rookie Mistake You’re in the middle of building a feature, committing after every tiny change:
bash
feat: start login page
fix: button not centered
fix: forgot to import styles
fix: typo in header
Then you open a PR… and your reviewer sees 15 commits for what could have been just one clean commit. Squashing them makes your history neat and your PR easier to review.
In fact, most large open source projects (think React, Node.js, Linux kernel) strongly prefer contributors to rebase and squash commits before merging. Why? Because a clean, linear commit history makes debugging, code archaeology, and cherry-picking fixes far less painful.
The “Oops, I Leaked Something” Scenario You’re happily coding, testing features, and then… you commit something you shouldn’t. Maybe it’s a .env file with API keys:
commit log
feat: add payment integration
+ .env
STRIPE_SECRET_KEY=sk_test_123abc...
Five minutes later, you realise: "Oh no, I just committed secrets!" 😬
You quickly remove the file and commit the fix:
bash
fix: remove .env file
But here’s the problem, that first commit still exists in your Git history. Anyone who clones the repo or browses the history can still find those credentials.
How git squash (and rebase) can save you Before pushing your branch to GitHub, you can rewrite history to make it look like the bad commit never existed:
bash
git rebase -i HEAD~2
Mark the “add payment integration” commit for edit or squash.
Remove the .env file from that commit.
Save and continue the rebase.
Now your commit history looks like the image below [SHOW IMAGE],clean, with no secrets ever added:
Immediately revoke the leaked key.
Force-push only if the project maintainers agree (open source projects are often strict about this).
Squashing is powerful — but like all power tools, it can also make a mess if you use it carelessly. Here’s how to do it safely:
Golden Rules for Safe Squashing
Squash before pushing to a shared branch
If no one else has seen the commits yet, you can squash without breaking anything.
If you’ve already pushed — check if it’s safe
Only squash commits that you alone have pushed and no one else has pulled yet.
If in doubt, ask your teammates or check your PR activity.
Use --force-with-lease
instead of --force
bash
git push --force-with-lease
This makes sure you don’t overwrite someone else’s work by accident.
After the branch has been merged into main — At that point, the history is shared. Squashing would rewrite it and cause conflicts for everyone who pulled it.
On branches other people are actively working on — If you rewrite history while someone else is based on the old commits, you're setting them up for a rebase nightmare.
When each commit is intentionally meaningful — Sometimes commits tell a story, like a multi-step migration, and you want to keep that history intact for future debugging.
git squash isn’t a command you’ll run every day, but when you need it, it’s like having a “commit history eraser” in your toolkit. It keeps your Git history clean, your pull requests readable, and your mistakes… well, a little less permanent.
The key takeaway?
Squash before merging to main.
Only rewrite history when you’re sure it’s safe.
Use it to tell a clear story of your changes, not to hide important details.
The best developers don’t just write good code, they also leave behind a history that makes sense to anyone who reads it later. git squash helps you do exactly that.
So next time your branch gets a little messy, don’t panic. Breathe, squash, push — and enjoy that satisfying, tidy commit log.
Feel free to reach out to me if you're looking for a developer, have a query, or simply want to connect.