The pull request is approved and the button offers a choice: merge commit, squash, or rebase. The code landing on main is identical either way; what changes is the story main tells afterwards.
Merge commit: keep the branch as it was#
A merge commit preserves every commit on the branch plus a marker joining it to main. Choose it when the branch’s commits are already clean - tidied with an interactive rebase, each one a real step. Main keeps the full context, and git log --graph shows the branch as a readable bubble.
The cost: if the branch was full of “wip” and “fix”, all of that lands on main forever.
Squash: the branch becomes one commit#
Squashing collapses the whole PR into a single commit on main. Choose it for the common case: a feature branch with exploratory, fix-as-you-go commits whose value was the journey, not the record. Main gains one commit per piece of work, which is exactly the granularity a changelog and a git log --oneline scan want.
Two things make squashing work well. Write the squash commit message properly - it is the only message main will keep, so give it the conventional prefix and a real summary. And delete the branch after merging: pushing more commits to a branch that was already squash-merged produces a follow-up PR full of commits main already has, because the squashed commit shares no history with them.
Rebase merge: linear, commit by commit#
Rebasing at merge replays the branch’s commits onto main one by one, no merge bubble. Choose it for small PRs that are already clean and where the team wants a strictly linear main. It asks the most of the branch - every commit lands verbatim, “fix typo” included - so it only suits branches that were tidied first.
A rule a small team can keep#
- Branch is messy (the usual case): squash, write a real message, delete the branch.
- Branch is clean and the steps matter: merge commit.
- Branch is clean and tiny: rebase is fine, but squashing it is fine too.
Agree on it once, write it in the README, and the choice stops costing a thought per PR.
Share this post
Keep reading
A First CI Pipeline for a Laravel Project
Install, lint, test, build - four jobs that run on every pull request and turn 'works on my machine' into a check everyone can see.
A Staging Environment Worth Having
Staging only catches what it shares with production. Same deploy path, same services, sanitized data - and a clear rule for what it is for.
Branch Naming Conventions That Scale Past Three People
Branch names are a user interface for your team's work. The conventions that survive growth, the ones that collapse, and why boring always wins.