Git is a versatile revisioning tool, allowing many different kind of manipulations. This versatility however also leads to the fact that you can achieve the same goal in different ways. An example of this is my previous blog post: Branch/Merge Strategies. In this blog post I will discuss something similar, yet different… the usage of the master branch.
When creating a new Git repository it initialises itself with a ‘master’ branch although it officially does not exist yet (since there are no commits). Yet the fact still remains that Git assumes a default branch names ‘master’, whereas all other branches you have to create yourself. This has lead to the approach of creating separate issue branches and even release branches. But an approach I have seen recently is the creation of a ‘develop’ branch, and I find it hard to identify the usage of this branch.
An issue branch has a clear purpose, containing all the code changes required to fix the issue, or implement the new feature. I can even understand the usage of version branches if you need to support multiple/older versions. And from what I have seen, the develop branch is often used in a similar fashion.
Instead of merging all of the code to the ‘master’ branch, instead the code is merged to the ‘develop’ branch. This branch contains the latest ‘develop’ version, and only after they want to create a new release version of it they will (optionally) create a new branch for the release to stabilise and once done is merged into master.
A visual representation is shown on the image above. On this image the ‘master’ branch is shown in grey, the ‘release’ branch is blue and the ‘develop’ branch is yellow.
The main concern I have with this approach is that it becomes impossible to make bug fixes to an older released version. To compensate for this the usage of tags is often done. Where a tag is used to indicate the released version. This does however not fix the problem, having a tag makes it possible to see in which version something was fixed but it does not allow to make changes later on. While theoretically you can checkout the tag, and start making changes but how do you get those changes back on the ‘master’ branch? Clearly you can only do it on the develop branch, meaning it will arrive on a possible much newer and incompatible version.
A more appropriate approach in my opinion is using different release branches, where you have a release branch for each major version and you guarantee compatibility between minor releases. This way the customer is guaranteed he can upgrade to a newer version without worrying about causing too much collateral damage. This opens up the master branch for you current work and only after it is done you will merge it to the release branch. And what about the release branch for stabilising? Well that was a wrong decision in the first place, because you should make sure whatever you commit is stable by itself.
The only question you could raise with this approach is how to work on the next release while still maintaining the old one? Since you only have a single branch (‘master’) and it will contain new features for the next major release as well as minor changes that need to be merged into the current release. Again there are multiple ways to deal with this, you can cherry pick the commits for the current release, or you can consider the master branch as the branch of the current major version. This means that master will always be the latest non-released version and work on other older major versions is done directly on that branch. This means you work on the ‘master’ branch until it is done to be released, after that you create a new branch and start working on the next release (on the master branch). Changes to the old release can than be done on that branch and be merged into the master branch, and either merge them into master directly or after a new release was created.
Visually it becomes like shown in the image above. The ‘master’ branch is shown in grey, the ‘release-1’ branch is shown in blue and the ‘release-2’ is shown in yellow. In my opinion it already looks better visually, since now branches are only merged in one direction.
By doing this, we did however create a new problem, what about keeping track of the versions? Since not every commit you do on the release branch means you have a new official release. This can be solved by using tags, or by keeping track of which version matches which hash (which is basically the same but in a more manual way).
One problem that is hard on both approaches is supporting multiple versions. With the first approach it was impossible to support older versions as you could not make any changes anymore without forcing the user to upgrade to the latest major version. The second approach allows the user to upgrade to the latest version of their current major version. However, fixes done on an older major version, somehow have to be merged into the new major version as well. Either by cherry picking all the changes done on the lower major releases, or by merging the lower major release into the higher major release as shown on the graph below.
Although this start to look like the first approach there still are significant difference. The idea behind this solution is that you always merge the major releases into the next higher major release. Creating a kind of waterfall model, where you fix bugs on the lowest major release on which it occurs. Eventually your highest major release is merged into master. Just note that dealing with many branches, and doing many minor changes on all of them will result in merge conflicts and can easily become hell. This is however not a problem caused by the chosen approach, but is intrinsic to the mechanism.