GitHub pull request (PR) provides 3 options for a PR merge. You can (1) regular merge, (2) squash merge, or (3) rebase.
Project can select the method to be used for every PR. For example, main
is often a protected branch, and it can set to use squash for all PRs merge.
There are trade-offs for all the 3 options. GitHub let you choose your poison, depending on your git flow and preferences. I will explain for each of the option, and conclude when to use which.
1. Regular merge
A regular merge is much similar to git merge
, except GitHub PR will be --no-ff
(no fast-forward) because it must create a new commit (not a big deal).
Unlike (2) squash merge, a regular merge will preserve all the commits. This is important because information such as author and will be correct. We need to git blame
correctly!
A good example is long-lived branch. I covered how to handle merge conflicts for long-lived branches, which is usually a BIG feature co-authored by multiple developers. When merging back to main, using regular merge will preserve the commit history and who changed what.
On the other hand, squash merge will not.
2. Squash merge
Squash merge will squash all the commits into 1 new commit, and the author will be the person creating the PR.
For the long-lived branch example, you should realize what problems this will pose:
git blame
will incorrectly refer to the author who create the PR, and not the original authorgit bisect
will not be able to identify the commit easily (because it is now 1 big commit)
That’s not all. Github also stated these disadvantages:
- … conflicts that you have to repeatedly resolve in each successive pull request
- … using
git rerere
may not be as effective
I covered git rerere earlier on how to let git automatically resolve conflicts. But rerere does not play well when commits are squashed!
If you encounter the same merge conflicts (eg. frequent merge from main), then squash could be the culprit.
Another side effect is that the squashed commit will only have 1 parent (to the base). This is unlike regular merge which have 2 parents (the base and the merge). A git log --graph --pretty=oneline --abbrev-commit --all
will show you the difference in the branches.
3. Rebase
GitHub PR rebase is different from git rebase
in 1 important aspect:
GitHub will always update the committer information and create new commit SHAs
It preserves the commits, but it has to duplicate them.
Another issue is that GitHub cannot sign these new commits. If you enable verified commits, this option can’t be used.
The benefits of rebase is often exaggerated. Yes, rebase produces a nice linear history.
But that is purely a nice visual, which doesn’t matter to experienced developers 😜 As such, rebase is definitely not a good option.
Conclusion - When to use what
The question boils down to when should you squash?
Squash only when it makes the history cleaner. Feature branch is a good example, because it is often short-lived, small, and authored by 1 developer.
On the other hand, use regular merge for big feature co-authored by multiple developers eg. long-lived branches.
Don’t rebase.