Squash merge all the things
I’m a huge fan of the squash merge strategy in GitHub and with git in general. Squash merging is a merging strategy where the commits of branch are combined (squashed) to a single commit. You should use it in your projects, here’s why.
The most obvious and immediate benefit of this approach is that the history of the main branch will be kept very clean and linear - no more “merge bubbles”. This happens even when your feature branch is not up to date with the main branch, because a single new commit will be added on top of whatever was last merged to main. When your project grows, keeping the main branch history clean is important because it’s vastly easier then to keep track of the changes between releases. Github adds the PR number by default to the squash merge commit message which also helps finding the associated PR.
Feature branches and whether to rebase or merge
The argument I’ve had most about using git in general is how to keep the feature branches up-to-date with the main branch. Either you want to integrate a feature from main in order to finish your PR or you want to ensure your branch works with main before it’s merged. You could do this either with rebasing or merging in the main to your feature branch, and I’ve noticed that in general devs prefer rebasing. Their argument usually is that they want to keep the history clean, without merge commits. It’s understandable, because if you merge multiple times, by the time you merge your feature branch to main you might have multiple merge commits, which is not nice.
The problem with rebasing is that it rewrites history. This is especially bad with PRs because code reviewing requires that someone else keeps track of the changes you are making. The reviewer usually has to come back and look at the fixes the PR author made after comments. Github even has this feature where it will track the changes the reviewer has seen. If you force-push to your branch this feature doesn’t work. It will lose track of the history (obviously, because you rewrote it) and it won’t show new stuff anymore.
I was once in a project where some devs kept browsing through the list of PRs and just manually rebased all the branches every now and then without asking the author. It was a big team, with a monorepo kind of a setup so the person doing the rebase might have been someone that didn’t know anything about what you were working on - let alone the project. It’s obviously ok to rebase and force-push to your own feature branch when it’s not public yet (no PR created). But that you, or let alone other people, rewrite the history when it’s being used by other devs just causes problems.
All of these problems go away when using squash merging. You can add as many “fixup” commits as you want, you can merge in the main branch as many times as you want. Go nuts with your feature branch, it doesn’t matter. Do whatever you can to make reviewing easier by not removing or editing commits and merge in the main branch if you have to. Squash merging solves this argument once and for all: never rebase!
Best practices with squash merging
There are some things that a team needs to pay attention when using squash merging.
When you do the squash-merge, make sure the commit message is edited a bit so that it’s not just a list of all the commit messages that are going to be squashed. Often it’s enough to copy and paste a part of the PR description if you have a habit of writing those (you should!).
Another important thing is to also run your full test suite and build in the main branch after merging in a feature branch. I’ve found out that it’s much better to do this instead of keeping the feature branches up-to-date with the main branch all the time. Even in a medium-sized team doing small PRs this becomes really difficult in practice. Better to just accept that the build can fail in the main branch and just fix it if this happens.
Lastly, squash merging only makes sense if you do small PRs that only do one thing. This basically means you can’t have long-lived branches and should practice trunk-based development.
You should consider using squash merging in your project because it keeps your git history clean and still allows for hygienic PR review practices by using merging instead of rebasing to keep feature branches up to date.