git.tools / guides

How to squash commits (and when not to)

Turn 'wip', 'fix', and 'actually fix this time' into one clean commit — with interactive rebase, GitHub's squash merge, or a pull request that does it for you.

Last updated September 18, 2026 · 3 min read

Nobody's first draft of history is pretty. You end up with wip, fix, fix fix, actually works now, and one commit titled asdf. Squashing folds a run of commits into a single one with a message worth reading.

Two ways to get there — and a fork in the road depending on whether you've pushed.

Way 1: interactive rebase (local, before pushing)

git rebase -i HEAD~4

Your editor opens with the last four commits listed, oldest first, each marked pick. Leave the first one as pick, change the rest to squash (or fixup if the commit messages deserve to die). Save, and git walks you through writing the combined message.

The result: four commits become one, with new SHAs. That's the important part — the old commits are replaced, not edited. Which leads to the rule:

Never rebase commits you've already pushed and someone might have pulled. You'd be swapping out commits their history depends on, and git will repay you with diverged-branch hell.

Way 2: squash-and-merge on GitHub (whole PRs)

GitHub's "Squash and merge" button folds an entire PR into one commit on the target branch. Zero rebase skills required, no force push, and the PR itself keeps the full messy history for archaeology.

The limitation: it's all-or-nothing per PR. You can't squash just the last six commits, and you can't squash a range sitting in the middle of a branch. For that you're back to rebase — or the tool below.

The git.tools way

Squash with a safety net

Pick the range of commits, write the new message, and git.tools opens a pull request containing the single squashed commit. Your original branch stays untouched until you merge — if the result looks wrong, just close the PR.

Open Squash →

When not to squash

Squashing is for noise, not for signal. A PR that fixes three distinct bugs is better as three commits — future you, bisecting a regression at 2 AM, will bless every clean boundary you kept. Squash the stumbles; keep the story.

Also know that squashing erases individual commit signatures and collapses authorship. If a teammate's work is in the range, keep their name on the result with a Co-authored-by: line. It's ten seconds and it's the decent thing to do.

The escape hatch

Did a rebase go sideways mid-squash? git rebase --abort takes you back to before you started. Finished the rebase and hate the result? The pre-rebase state is one git reflog lookup away. And if you'd rather not need escape hatches: a squashed PR you can review before merging is the version of this operation where nothing is ever at risk.

Common questions

Can I squash commits I've already pushed?

Technically yes, but it rewrites history, so you'd have to force push. On a branch only you use, that's fine with --force-with-lease. On a shared branch, don't — use a squashed PR or GitHub's squash merge instead.

What's the difference between squash and fixup in interactive rebase?

Squash melds the commit into the previous one and lets you combine both commit messages. Fixup does the same meld but discards the commit's message entirely — right for 'typo' and 'oops' commits.

What happens to the original authors when I squash?

The new commit gets a single author, so add Co-authored-by: Name <email> lines to the message for everyone whose work is folded in. GitHub renders these as multiple authors.

Should every pull request be squash-merged?

It's a team taste question. Squash merging keeps main linear and clean, but destroys granular history that can be gold during debugging. Squashing 'wip' noise is always good; squashing ten meaningful commits into one blob is a loss.