Branching and Release Model
Introduction
Please see Re-evaluation of branching strategy for additional context.
The DPLA Tech Team has decided to follow the OneFlow Git branching model where it is relevant to our operations. Please consult it for details and illustrations. This wiki page will cover its usage as it applies specifically to the DPLA's projects.
This model supersedes our earlier model that is documented in our old Run Book's Release Process pages and our old Software Release Process page. There are a number of older repositories that we're not migrating to this branching and release model until we need to do further work on them. You may encounter repositories with either the old or new model. The old ones will be apparent by the existence of a develop
branch in addition to master
.
The "Release" As It Applies to the DPLA
What does a software release mean to us? In most of our software projects to date, it means nothing more than assigning a version number – a tag – to the master branch of one of our repositories. We do not maintain the sort of software that has multiple legacy releases that receive updates and have long-term-support schedules. We don't ship shrink-wrapped software. We mostly support our software through a single branch (usually master) that progresses in one line, without long-running maintenance branches. We don't produce release candidates and update release branches as bugs are caught over time by the community, or by a separate QA department.
For those reasons, we don't need to create release branches anymore, as we used to when we worked with the GitFlow branching model and documented our methodology in our old Run Book and, later, in our Tech Team wiki.
Eternal Branches, Tags, and Deployments
The eternal branches in one of our new-model repositories are master
and, in some cases, latest
. We don't want there to be any confusion over whether the GitFlow model or the OneFlow model is in use, so we're no longer keeping a branch named develop
. When it exists, latest
is a convenience for deployment purposes. Instead of looking up the most recent release version number, someone can do a git pull
from our latest
branch. This is discussed in OneFlow and below.
Our policy for deployments of any of our applications is to follow our master
or latest
branches if you want to pull from one git ref that you don't have to keep up to date. We won't merge anything into master
that we don't think is stable, but if you want to be extra careful (as we are when we deploy to production) you can pull from latest
. If you require predictability between deployments or installations on different hosts over time (e.g. server 1 today and server 2 tomorrow) then we still tag releases, and we still use semantic versioning, so clone or check out a release tag as before, and update the release tag manually whenever you configure deployments.
Examples
Creating and Maintaining a latest
Branch for Convenience
git checkout -b latest 1.0.0 git push -u origin latest
Time passes. Commits are added to master
. A new version is tagged on master
...
git checkout latest git merge --ff-only 1.1.0 git push
Adding a Feature
git checkout -b feature/x master
Make changes – say multiple commits for this example. Test. Do a pull request and code review.
git rebase -i master # Squash commits prior to merge. Usually better control doing this in a shell. git push -f feature/x # We assume now that we'll do a merge using GitHub's Pull Request U.I
Merge the P.R. on GitHub. Delete the feature branch from the remote on GitHub.
git checkout master git pull git branch -d feature/x # Delete your local copy of the branch.
Releasing
This assumes that the release is of the last commit on master
. It also assumes you have a GPG key for git tag -s
.
git checkout master vi VERSION # Or Changelog, or whatever, to bump the version number, etc. git commit -a -m 'Prepare Release 1.0.0' git tag -s -m 'Release 1.0.0' 1.0.0 git push origin master 1.0.0
If you ever need to go back and tag a particular commit that is not at the tip of your branch (which might be the case if you forgot to do so in the past):
git tag -s -m 'Release 1.0.0' 1.0.0 9efc5d # Where "9efc5d" is the commit ID from your git log.
Hotfixing
Hotfix branches are discussed in the OneFlow page.
This could be relevant if we're working on a multi-sprint set of changes where we want to merge individual commits for individual stories when they are completed, but when we're not ready to deploy the tip of master
to production yet.
In this example, which tries to illustrate a number of points in one go, there are commits on master tagged as 1.0.0 and 1.1.0. We want to fix something that wants to go out the door as 1.0.1. In most cases, we won't be doing long-term support for past versions, so this won't apply exactly as illustrated below, and we'll just be making a hotfix branch off of 1.1.0 in order to keep more recent commits on master
out of the picture.
git checkout -b hotfix/x 1.0.0 # Where 1.0.0 is the release we want to fix
Here you'll make changes (say multiple commits, working to get things right), test, and perform a code review with a pull request. The pull request may be closed without merging into master
; which will be expanded upon below. When the pull request has been approved ...
git rebase -i 1.0.0 # Squash, etc.
Note how that rebase is going against 1.0.0
and not master
.
With the hotfix/x
branch checked out, you could use git describe HEAD
to double-check its upstream tag for rebasing against. If it says something like 1.0.0-2-g1587760
then 1.0.0
(the part before the first hyphen) is the ancestor tag.
Now with the branch squashed, you should edit the VERSION or Changelog file, or whatever files are modified to bump the version number and record a release. You're bumping the version in this example to 1.0.1
. Remember you branched off of that older version, so expect the version string, Changelog, or whatever to say 1.0.0 before you increment it. Also note that you're not going to blow away version 1.1.0
or any of its changes – stay tuned.
vi VERSION # Or Changelog, or whatever git commit -a -m 'Prepare Release 1.0.1' git tag -s -m 'Release 1.0.1' 1.0.1 hotfix/x # You are tagging this here on the topic branch! git push -u origin hotfix/x 1.0.1
At this point, the hotfix and its tag will be available to the public and the hotfix/x
ref will exist publicly. The commit for the hotfix will always exist even when you later delete the hotfix/x
ref. You can merge the hotfix branch back into master
in GitHub's Pull Request page. There may be some reason why it's not desirable to merge this into master
yet, but it is usually going to be a good idea to do so. You probably want to fold the fix back into the code that is currently under development and scheduled for the next release.
When you are ready to merge the hotfix branch to master
:
git checkout master git merge hotfix/x
If there are conflicts merging the hotfix back in (such as when there have been commits to master
between version 1.0.0
and the merge) then the changes necessary to resolve the conflict will be captured in the merge commit. The commits that actually fix the problem are unadulterated by any changes necessary to resolve conflicts with master
– they only show changes with relation to version 1.0.0
, because that's the ref that we branched off of above, and then rebased against. This is better than if you'd rebased against master
before merging into master
.
During this merge back into master
, and resolving merge conflicts, it can be helpful to run git show 1.0.0:myfile
(where "myfile" is the path relative to your current path) or git show master:myfile
or whatever, if the conflict markers in the file are confusing.
At this point, with the hotfix's merge back into master, you just need to tag version 1.1.1
on master
and release it. See above under Releasing.