There are two important features that we require from a branching model: simplicity and release composition.
We have a dynamic team. The code owners and release a mangers change frequently. Further, our engineers wear many hats. None of them specialize in release or code management. We need a branching model that is easy to grok and limits the possibility of human error. Github Flow provides a simple branching model. But, having individual developers be responsible for their own releases didn't match well with our engineering processes and merging adolescent code to master is more cavalier than we are comfortable with. However, the single long lived master branch in Github Flow is compelling and appeals to our desire for something simple.
One of the problems we often encounter is what I'll call "release logistics". Marketing may want to release a new change to our app tour that they hope will increase stickiness. Sales may want to release a security feature that will impact a new opportunity. Our Customer Success Team may want to release features that have been requested by their enterprise customers. The Customer Solutions Team may need a fix to ship as soon as possible. The Product Team often wants to change the order of when features land so that they can curate the user experience just so. The reality is that scheduling changes is not an engineering problem alone. Sometimes, it's not an engineering problem at all. Scheduling change is often a business problem. So, for us, we can't just merge to develop and wait for the conveyor belt to bring the change to production. We need to compose releases from a set of staged features that are ready for release.
Shift Flow is intended to support parallel development branches while maintaining the flexibility of what we include in a given release.
Shift Flow has a single, long lived branch,
master. Master contains a commit for each release tagged with the semantic version. We rebase changes onto master only after a release. Master itself is never released. You should always release from a release branch even if you want to rollback to a previous release. In this case you should create a release branch from the previous tag and release it.
git fetch && git fetch --tagsgit checkout -b release/1.0.0 v1.0.0
We do this for consistency purposes. It helps everyone involved know that branches with in the
release namespace are being prepared for release.
Since we use a single long live branch rather than two, creating branches for features and hot fixes is the same. Features should be under the
feature name space an hot fixes should be under the
hotfix namespace. Features an hot fixes should be further name spaced under the ticket they are addressing so that we can easily tie the change to the request in our ticketing system.
The branch naming convention for features is
feature/TICKET_ID/SHORT_FEATURE_DESCRIPTION. For example,
The branch naming convention for hot fixes is
hotfix/TICKET_ID/SHORT_FIX_DESCRIPTION. For example,
When you are ready to create a new feature or hot fix you should create a new branch off of the tip of master. This branch should be used for review by your peers and product for validation.
You should create a new branch like this:
git checkout mastergit pullgit checkout -b feature/SHIFT-1234/create-new-login-form
If you need to keep your branch up to date with master you should rebase. First make sure you have the latest master. Then...
git rebase master
Commits on Feature and Hot Fix Branches
During development, commit as often as you'd like and describe those commits in any way that works for your development process. When you're finished development squash your commits into the format
CHANGE_TYPE(FEATURE): DESCRIPTION. For example,
fix(Navigation): Don't open new window when clicking a nav menu item
When you are ready to compose a release a new branch is created off of the tip of master. The branch should be name spaced under
release/. The branch itself should be named after the expected version of the next release. For example:
release/1.0.0. Any features that you'd like to release should be rebased onto this branch. This is the branch that you test. This is the branch that you release.
You should create a new release branch like this:
git checkout mastergit pullgit checkout -b release/1.0.0
Let's put this into practice with an example. Assume we have two branches:
feature/f2. Let's also assume that both of these branches have gone through validation and we would like to test and release them as version 1.2.0. First we should create a new release branch from the tip of master.
git checkout mastergit pullgit checkout -b release/1.2.0
Next, we want to rebase each feature branch onto the release branch so that it's new base commit is the most recent commit from the release branch.
# Make the most recent commit on release/1.2.0 be the base commit for the feature/f1 branchgit checkout feature/f1git pullgit rebase release/1.2.0# Apply the commits from feature/f1 onto the release/1.2.0 branchgit checkout release/1.2.0git rebase feature/f1# Make the most recent commit on release/1.2.0 be the base commit for the feature/f2 branchgit checkout feature/f2git pullgit rebase release/1.2.0# Apply the commits from feature/f2 onto the release/1.2.0 branchgit checkout release/1.2.0git rebase feature/f1
Lastly, we need to push our release branch to origin.
Once you've release your code to production you need to tag master so that we know that the version you've just released points to a specific commit in our commit history.
git checkout mastergit rebase release/1.2.0git tag -a v1.2.0git push --tags