In a new project it is always necessary to choose a strategy for working with your version control system when it comes to branching and release management. Some of the things I look for in a branching strategy:
- It should be as simple as possible.
- It should maximize the benefits of continuous integration.
- It should make it easy to create a release.
For Git, a strategy that has been used in many projects is GitFlow. This post will look at some aspects of GitFlow and propose a simpler branching strategy.
It is important to remember that GitFlow was initially described in 2010, when manual releases were common, and is based on the idea of merging changes that should go into a release into the master branch as preparation for a production release. This means that GitFlow is not well suited for continuous delivery. In my opinion, most projects should strive for being able to do continuous delivery, even if the system is actually released in long cycles.
In GitFlow, all development is done on a develop branch, and the work is merged into the master branch as a part of the release process. The idea is that the master branch should always contain code in a production-ready state.
What is the benefit of always keeping the master branch ready for production? You should never deploy from the head of a branch anyway, you should always deploy from a tag. This means you could do development on the master branch instead, and tag it when it is ready for production.
Conclusion: Do not use a develop branch, do development on the master branch.
Feature branches are used to let developers work on a feature without being disturbed by the work of others. But when we use continuous integration, isolating the work that different developers do from each other is exactly what we want to avoid! All work that is being done on a branch that is not continuously integrated brings us a step closer to a miniature “integration hell”.
The alternative is to do all work on the master branch. This requires a clean code base with high cohesion and low coupling, as well as constant communication between the developers, so that developers rarely have to work on the same bit of code, and know when they do.
If a feature is large, it can either be delivered incrementally or hidden from users until it is ready. If it is necessary to make a large-scale change that affects a large portion of the code, you can use the Branch by Abstraction pattern as an alternative to creating a Git branch.
It is often useful to keep track on the changes that have been made for a specific feature. Instead of using feature branches, this can be achieved by adding the ID of the feature to the commit comment. If you are using JIRA, for example, a Git integration plugin makes it very easy to see all commits that belong to a certain issue.
Conclusion: Do not use feature branches, do development on the master branch, using small incremental commits. Every commit message should contain the ID of the feature, bug, improvement or similar being worked on.
Release and Hotfix Branches
In GitFlow, a release branch is created before each release, and any release preparation is done on the release branch, including updating version numbers to match the release. The release branch is only kept until the release is ready, when it is removed.
A hotfix branch is created if it is necessary to make a change in a system that is in production. It is created from the tag of the released system and used for making the fix, after which the hotfix branch is removed.
We do need a release branch to prepare our release, and we may also need a branch to make fixes to the release after it has been taken into production. However, it is not necessary to create separate branches for the different purposes, instead we can create a release branch where we do the release preparation and let the branch live indefinitely in case we do need to make any fixes to that release.
Conclusion: Create a release branch before each release and let the branch live indefinitely. If it is necessary to make changes to the release, do them on the release branch and make sure the changes are merged into the master branch.
Automating the Release Procedure
As an example of how the creation of the release branch can be automated, here is how the continuous integration system can be configured to support continuous delivery where each commit is a potential release.
First some build parameters:
major.version.number=1.0 version.number=%major.version.number%.%build.counter% release.branch=release-%version.number%
Now the build steps:
# Create a release branch git checkout -b %release.branch% master # Update version numbers mvn versions:set -DnewVersion=%version.number% # Build and run tests mvn -P checkstyle,findbugs,integration-test -U clean install \ enforcer:enforce # Commit and tag release git commit -a -m "New release candidate %version.number%" git push origin %release.branch% git tag %version.number% git push origin %version.number% # Remove local branch git checkout master git branch -D %release.branch%
- Avoid branching as much as possible. Do the development work on the master branch to get the most possible benefit from continuous integration.
- Use small incremental commits, constant communication and a clean code base to avoid problems with developers working on the same piece of code.
- For major changes, use incremental delivery, feature hiding, or Branch by Abstraction.
- If there is a problem that needs to be fixed in a system that is in production, first of all investigate if it is possible to make the fix only in the master branch and release a new version into production. If not, do the fix in the release branch corresponding to the version in production and merge the fix into the master branch.