A branching model or strategy should be as simple as it can be while
still supporting your current use cases. To that end we need to start
with a simple strategy and only make it more complex as the need arises.
The simple case: GitHub Flow
There is one eternal branch. We can call it master.
- All feature branches are created from
master and merged back into it
- When a version is to be released tag that commit
How this relates to your strategy
The way you describe your branching is already like the above
description, only with more branches:
- About to start work on a new version: make a branch for that
- Feature branches start from this branch and are merged back into it
- “When release X is completed, merge it into master and tag master
branch with X”
Notice that there are no points where you might merge into master
instead of the release branch. So master never gets updated. This in
effect means that this new release branch effectively works like master
until the final release.
And the crucial step is (3): the release branch is merged into master
and then tagged (released). Thus for the release itself (the tagged
commit) it made no difference that you created a new branch for the
upcoming release.
So the release branch is just one more thing to manage. Both for the
person who manages it and for all the people who have to keep in mind to
branch off of that new branch.
But there is one chink in my explanation here: you create the next
release branch from the previous release branch before the release of
the first version (12.1 vs. 12.2). Now we might get into a legitimate
need for a dedicated release branch. But on the other hand I can’t quite
make sense of it. Why start a release branch the branch of 12.1 (yet to
be released)? It seems random. You’re effectively starting the release
branch of 12.2 from some spot in between 12.0 and 12.1.
The need for release branches: diverging master and the upcoming release
In general: there is a need for a release branch if the eternal master
branch and the release branch might diverge. Concretely:
- You have four pending feature branches which you want in
master
- But two of them are risky so you don’t want them in the upcoming
release
Hence you will get a divergence: master will have something that you
don’t want in the upcoming release.
And here is where you can create a release branch. Let’s call it a
“release candidate branch” (RC) so that I don’t get confused between the
final release and work-in-progress towards the release.
And this is the time to make the RC branch 12.1-rc from master.
Now you can merge in the two safe things into 12.1-rc branch. Let’s
say this happens over a few days:
- Risky 1 is merged into
master
- Safe 1 is merged into
12.1-rc
- And RC into
master to keep master up to date
- Risky 2 is merged into
master
- Safe 2 is merged into
12.1-rc
In this way your development can contiue as normal without interrupting
the upcoming release. Meaning you can pick and choose what goes into the
next release.
Then after 12.1 is released you can merge the RC branch into master
or the other way around (this depends on what kind of first parent
merge history you want). Delete the RC branch.
Updating releases
As we have discussed: only create as many branches as you
need. Sometimes you won’t need a release candidate branch. And you might
not need to update a release.
But say there is a bug in 12.1. You don’t want the installations that
are on this release to get the next upcoming version yet (say
12.5). So create a branch from that tag, maybe 12.1.1-rc, do the
hotfix and release it.
Updating later releases: backporting
You already explained a great strategy for backporting fixes:
If a hotfix is needed, create a hotfix branch from oldest release
branch that needs it, commit changes, and merge back into all release
branches for that release and future releases that could be affected;
if the latest stable release branch was affected, merge it into
master.
Exactly: the fix goes to the oldest release and is propagated forward as
needed. Do you have four other releases that need this one as well?
Back-merge them upwards. Then finally merge the latest merge to
master. Or perhaps you don’t need to update any more releases? Then
back-merge directly to `master**.
Just delete branches that are no longer needed
The workflow models I've seen seem to not keep release branches alive
much, once done the release gets merged into master, tagged, and
removed. My problem with that is that I don't have a good idea how to
manage the state of the release if all I have are tags in master,
seems easier to hotfix in a branch and then I have a release I can
always go back to that has the latest hotfix (I can even tag the
hotfixes in the release). Not sure there's a way I could go back
within master and somehow have a copy of the release with hotfixes
applied and update that tag.
You don’t need to keep around branches that are not in use.
Say you release 12.1. There might be no issues with it. And if there
are? Create a branch from that tag.