A while ago I encountered a GIT feature workflow (BPF) proposed by Adam Dymitruk. This workflow aims to provide very fine-grained control over what features get released at the end of a development cycle. The workflow is a natural fit for a SCRUM development method. I read all the comments and information I could find and although the general approach was clear to me, I was not entirely sure how to actually implement the workflow. After some more researching I found this blog post by Aquia. Most puzzle pieces fell into place after reading it. There were however still some issues that in my mind would prevent a rapid adoption within my organisation. This blog post is about those issues and how tried to solve them with my specific implementation and automation of the workflow.
The BPF workflow requires the following branches: master, integration, release and feature(s).
The master branch contains the only production-ready code. This code has been tested and deemed stable enough to release to production. The master branch never receives direct commits, it only receives release branch merges. This is very important since we don't want to allow untested commits to be deployed to production.
The integration branch contains all the development code. This branch also never receives direct commits, it only receives merges from feature branches that developers are working on. The sole purpose of this branch is to detect possible merge conflicts as soon as possible. Every developer should merge his/her feature branch to the integration regularly so that any conflicts are detected and fixed immediately. The Aquia blog post explains very well that you should never deploy from this branch, it is not suitable for deploying to the test environment. This branch may be recreated any time. As such, you would never base work off of this branch.
The release branch contains code that is ready for deployment to the test/staging evironment. This branch also never receives direct commits, it only receives merges from feature branches that developers have completed. The release branch is used to build a release candidate for the current development cycle. It may be recreated at any time with additional or removed feature branches. This is where the strength of the workflow comes from: it allows for very grained control on which feature branches make it into the final release. An automated recreation process is needed since it must potentially be recreated often. The Aquia blog also clearly explains that every developer should be able to recreate the branch. This adds complexity since any developer must now be able to solve merge conflicts for code which he/she did not necessarily create. This blog post details a possible solution to this problem.
The feature branch contains development code that directly corresponds to an issue in your issuetracker. Feature branches should be 'atomic' in the sense that they should only concern themselves with one particular bugfix, feature, etc. Code from other feature branches should never be merged into the feature branch. This is what allows the release branch to be recreated with independent features since there are no dependencies between feature branches
In my opinion there are a two problems with the practicality of this workflow.
Sharing the rerere cache
It is likely that we will be fixing the same merge conflicts over and over again since the integration- and release branch can be recreated at any time. Most conflicts will be resolved on the integration branch and these resolutions need to be shared within the team, so that other developers can solve the conflict automatically. Git provides a mechanism for recording conflict resolutions called 'rerere'. Unfortunately these resolutions are local to the repository and need to be shared to the other team members through some sort of mechanism. Adam recommends to share the 'rerere' cache through a dedicated repository or branch where all developers push and pull their resolutions to. The same approach is taken by the Aquia developers. It requires installing hooks into the local repository which update the 'rerere' repository. I think this approach is very cumbersome.
Recreation of the release branch should be automated
The release branch should contain (or remove!) the previous feature branch merges when it is recreated. The recreation is necessary to make sure that we include all the latest changes from feature branches. However, we don't want the developer merging every feature branch back into the release branch by hand. An automated solution is necessary and the developers at Aquia have created a Ruby gem for this purpose.
The 'git release' command
I've recreated and improved the Aquia script in Python to hopefully allow for a wider adoption of the BPF workflow. For the most part it is a copy of the Aquia gem but I've added the facility to 'learn' resolutions from previous merge commits. This functionality is also nothing particularly new, it has been in the Git distribution for quite a while in the form of the 'rerere-train' script. I've integrated these two scripts so that sharing of a rerere cache is no longer necessary. Developers can now pre-fill their rerere cache from the integration- and release branch if necessary. All previous resolutions will be recorded from the merge commits and used to automatically resolve conflicts when recreating the release branch. A full description of the script and its parameters is provided in the Github repository. This resolves the above mentioned difficulties and provides a smooth way to manage the release branch during a development cycle.
Branch-per-Feature and the Scrum process
This section described the workflow and naming scheme we have implemented at my organisation. At the start of sprint we create a 'sprint_X-start' tag. The 'X' is replaced by the sprint number that we are currently in. This tag is the based on the latest deployed code (master) and is used as the base for all other branches that are created during this sprint.
Next, we create the integration branch called 'sprint_X-integration'. The integration branch is recreated with a different number at the start of every sprint.
The release branch is created in the same manner as the integration and called 'sprint_X-release'.
We now have two 'sprint' branches that exist for the duration of the sprint. Developers will create feature branches based on the sprint_X-start tag that correspond to our Jira tickets during the sprint. Every developer is constantly merging their development code with the integration branch to catch any conflicts early.
The 'git release' script is used to manage the release branch during the sprint. Features are added and removed. A deployment occurs everytime we update the release branch. The release branch is merged to the master branch when it has gotten final approval at the end of the sprint and subsequently deployed to production. This triggers a new release cycle with a new tag and branches.