A lot of teams follow these rules. The question is why!?
The rules are:
- Use GitHub flow – i.e.
masteris the stable trunk
- Everything else should be a feature branch
- Feature branches are merged to
masterwith pull requests
- No matter how many commits are made to achieve the last HEAD of a feature branch, they should all be squashed into a single commit before merge
- The merge should be made after a rebase and retest of the feature branch – in other words, merge only one-commit-from-HEAD-of-master
- Depending on the team the merge itself might then be:
- A fast-forward-only on
- An actual merge (even if fast-forward is possible, which it must be)
- A fast-forward-only on
This leads to a
git history which looks like a straight line with no weird crossing of the streams. In the fast-forward-only approach, it’s like there were no branches. In the merge approach, there’s a series of detours in the commit history, showing the vestigial remnant of the branch that existed.
Why are we doing this? Especially since the rebasing and retesting can be very time consuming:
- Genuinely testing the commit that will exist on
masterbefore it reaches
- Make easily revertible changes
- Explain the history to people reviewing the code after the event
Of these, the most important is probably the first. Though some build systems cheat and perform a secret merge before they build, the problem with a working branch and a working
master is that the intersection of them isn’t necessarily a working
master. It could be, but it might not. Merge conflicts alone are not enough of a measure of likelihood of success.
Forcing ourselves to retest the merge before it becomes the head of
master is a very good idea.
But there’s more?
Why Rebase not Merge Though?
Git will let you spaghettify your merges and connect lots of dots together. You can become inured to resolving merge conflicts, only to do it again next time.
Merges have their place, but rebasing is a cleaner way to rewrite the history where there’s everything that’s gone before and then plus my stuff. That’s much easier to reason with.
When there’s been a spaghetti festival near the HEAD of
master and there’s need for a revert, it becomes quite difficult to know what to revert to what, and how to get things back into a clear state.
The ideal revert involves resetting
master to a state where the offending commit is no longer in the code base, and then dropping that commit into a fresh feature branch for its authors to review in order to work out how to fix it. Doing that from spaghetti is relatively insane.
Similarly, for telling the story of how the code was evolved, the long cross lines of merges from around the tree may tell how hard it was, but don’t really explain the intent of the increments. Simple individual rebased commits do.
Keep your git commit history idealised and it will always help you.