Branching workflow: git-flow and github-flow
Choosing a branching model for macchiato
Lately, I’ve been working on Macchiato to bring web application development libraries for ClojureScript on Node.js. Get a few people of different backgrounds involved on a project, and pretty soon a discussion about methodology will emerge.
Since both Dmitri Sotnikov and myself are working on some libraries at the same time, we had to decide which approach to use.
There are two major alternatives: Git-flow and Github-flow (with Gitlab-flow being a slightly more elaborate version).
Let’s review them.
Git-flow was described by Vincent Driessen on his 2010 post A successful Git branching model. I’ve found it to be a great way to organize a repository. In short, you:
- Keep your
master branch as the code that has been released,
- Use a
develop branch as the current “snapshot” of what will go into the next release - your living beta,
- Spawn off feature branches off
develop for every new feature, which are merged back into it when they’re ready,
- When you are ready to release, you merge to
master and tag with a release version.
There are other considerations for how to deal with hotfixes, but that’s the gist of it. You can see a git-flow diagram below:
This is a great approach and it has several advantages:
master always remains as a stable reflection of your live code,
- You can trivially do hotfixes without worrying about unfinished features,
- Feature branches ensure that teams working in parallel don’t trip over each other and that conflicts need to be handled only once (the moment the feature is done),
- It allows teams to cherry-pick changes other teams may be doing on their feature branches, without needing these commits to be on develop already,
- It lets you do a release off develop at any point - if a feature isn’t ready, that’s OK, you just leave it for the next release.
There are helper scripts to assist you with Gitflow. Atlassian’s SourceTree supports it directly as well.
Proponents of Github-flow (and its cousin Gitlab-flow) have a few complaints about it. The main argument is that it goes against continuous delivery, since at some point someone needs to “flip a switch” and do a release from
That’s a valid argument. Nothing is less continuous than manual switch-flipping.
The Github-flow proposal is that you should:
- Do away with the
develop branch altogether,
- Spawn all feature branches off master, and merge them back into master when ready,
- Version tagging should not be carried by a human, but by an automated process whenever code is pushed to live,
These are valid points and they do result on a lighter-weight workflow. But they ignore that there are cases where you want a manual release, a negotiation of what is supposed to go into any particular version.
My take on it
Both systems have their uses. I think something like Github-flow is a great approach if what you are developing is an application. On that case, just using a master branch plus features branches works well, and you can do your releases in an automated fashion. The deployment itself is the atomic end-product you care about.
When you are building a library, where people outside your team might depend on specific versions, I think git-flow is the right approach. For a library, you want to lump your versioned changes together into a conceptual unit - including potentially separating or delaying breaking changes. At this point, coming to an agreement on what should go into a version makes sense, as does the manual release process of git-flow.
If anybody outside of your team depends on your versioning scheme, or will need to have a clear conceptual overview of what changed on a particular release, I’d recommend using git-flow.
If whatever you are releasing is the final artifact, and it will be used but others will not need to reference its version, then GitHub-flow does the trick.