Branching Strategies with GIT
Sooner rather than later every project has to be released. At that point it is quite common having to maintain the current public release with bug fixes while working on adding more awesome features to be released some time in the future.
Back in January 2010 a Dutch iOS developer named Vincent Driessen published what's now known as “GIT flow”. Driessen described their GIT setup with develop branches, hotfix branches, master branches… Very interesting read and it all makes sense but it's a bit complicated—at least to begin with.
Last summer Scott Chacon, from no other than Github themselves, followed up with how the so called GIT flow didn't quite fit the development model of Github.
The point is that there is no approach that fits all projects or clients or teams. You need to take a look to your requirements and take it from there.
Thus we thought we’d share our setup for a long ongoing project (over a year long), four to seven developers, close to no public releases for eight months, but after that has had releases every two weeks, after every sprint.
- A stable version of the app that can be released at any time. This is done from the last tag of what we call the release branch. It represents the version of the application that the public is using.
- A version of the app whose main focus is new features, not stability. This is the master branch, as per GIT conventions. Developers are free here to be more aggressive in terms of refactoring, etc.
- Minimize management overhead.
So we have a release branch, let's say 1.12.3 (more on version numbers later) and a master branch. As agreed between the team and the client, no new features are added to the release branch, only bug fixes. If the bug applies to both branches then it's fixed in release and "ported" to master. The technicalities of how that port is made are important, more on that in a second.
But, what is a bug and what's a new feature? Sometimes the line between them is a thin one, so the main idea is this:
**EVERY COMMIT TO THE RELEASE BRANCH SHOULD MAKE IT MORE STABLE, NOT LESS.
** Important things to note:
- No new features are EVER added to a release branch...
- ...but that doesn't mean we don't update with more features. It means that if we want to release new features, we branch off again from the master and create a new release branch. The old release branch is effectively "dead".
When we branch off master for the second time,
ALL THE FEATURES ADDED TO THE MASTER BRANCH SINCE THE LAST RELEASE WILL BE PART OF THE NEW RELEASE BRANCH.
It's very important that everyone in the project understands this. Our simple approach means that you cannot cherry pick what new features will be part of a new release. This is a conscious trade-off agreed with the client beforehand to keep things simple.
So is that all for branches?
Not really. The description above is only for the basic branch set up. Whenever a big feature, refactor or re-write needs to be implemented we do it on a specific branch, but we don’t have many rules around those. You simple branch off master (remember, no big changes in the release branch), rebase frequently and merge back to master when you are done.
major number . sprint number . bug fix revision number
- major number > increased every time there's a new public release.
- sprint number > the sprint the release was made of. This gives a quick idea of the features included (most software management suites like Trac or Rally should be able to tell you which stories you worked on a given set of sprints).
- bug fix version number > increased every time a new release containing bug fixes is made.
So, if our first public release was 1.12.0, after a few iterations with the QA teams and bug fix releases, we could be now on 1.12.3. Our next release public release could be 2.16.0.
Please note that these numbers are not necessarily for end users to see. You can of course put this information in the release notes or the “About” menu and show to the user a more marketing oriented name such as “Ice Cream Sandwich”,“Snow Lion”, etc.
How to bring changes from release to master
We looked at several ways of bringing common bug fixes from release to master and we settled for the not very appreciated
git cherry pick.
A cherry pick will bring the code of a given commit from one branch to another. The code of that commit and absolutely nothing else. Doing a merge or rebase from release to master could bring unexpected changes.
The downside of cherry pick over merge or rebase is that if the commit we are bringing over has a dependency on a previous commit that has not been already ported, then the application is likely to break. But by team rules this should never happen. Developers are expected to fix bugs with one commit only and, if it's a common bug, porting the fix to master also in one single cherry pick commit. It's worked great so far.
A suggestion to wrap it all up. Since GIT does not create a folder for the branch that is checked out, we recommend having different folders and projects in your IDE for different branches. E.g. a master folder and a master project and a 1.12.0 folder and 1.12.0 project. This way the branch name is "clearly displayed" as part of the path on the console (we use GIT on the console) and helps avoiding silly mistakes by working on the wrong branch (which otherwise you would only know by calling for example