GitFlow is probably the most popular Git branching model in use today. It seems to be everywhere. It certainly is everywhere for me personally – practically every project at my current job uses it, and often it’s the clients themselves who have chosen it.
I remember reading the original GitFlow article back when it first came out. I was deeply unimpressed – I thought it was a weird, over-engineered solution to a non-existent problem. I couldn’t see a single benefit of using such a heavy approach. I quickly dismissed the article and continued to use Git the way I always did (I’ll describe that way later in the article). Now, after having some hands-on experience with GitFlow, and based on my observations of others using (or, should I say more precisely, trying to use) it, that initial, intuitive dislike has grown into a well-founded, experienced distaste. In this article I want to explain precisely the reasons for that distaste, and present an alternative way of branching which is superior, at least in my opinion, to GitFlow in every way.
But mistakes aren’t even the worst part. What I consider the biggest failure of GitFlow is that it doesn’t give people a clear vision of a versioning scheme. This is especially true if you have any deviations from the standard workflow that GitFlow forces on you (for example, you have a long release cycle with a lot of back and fourth between QA and development). All of the mistake examples I gave above really stem from the fact that people are confused about what actually represents the current state of the project. Since they don’t really understand what that state is, then it’s no wonder that they make mistakes when they try to change it (as that is what publishing their work is actually meant to accomplish).
I want to describe an alternative method that I’ve used myself successfully on a number of projects (to be clear, I’m not talking about one-person projects). I believe it fulfills all of the goals that GitFlow set out to accomplish, and does it in a lot simpler, clearer and lightweight way which scales to any number of developers. You can call it “Anti-gitflow”, as it’s very similar in a lot of points that don’t need any change compared to GitFlow, but does the exact opposite wherever GitFlow falls short, as I’ve described above.
Here it is:
- There is only one eternal branch – you can call it master, develop, current, next – whatever. I personally like “master”, and that’s the name I’ll use in the rest of the description, as it’s convention by now in the Git world and immediately conveys it’s purpose.
- All other branches (feature, release, hotfix, and whatever else you need) are temporary and only used as a convenience to share code with other developers and as a backup measure. They are always removed once the changes present on them land on master.
- Features are integrated onto the master branch primarily in a way which keeps the history linear. You have a lot of leeway in how you want to enforce this. You can make it simply a convention that developers are encouraged, but not forced, to follow. On the other side of the spectrum, if you use something like Gerrit to manage your Git repositories (which I recommend, even if you don’t practice code reviews – the permission system is fantastic, and if you ever decide you want code reviews, it’ll be very easy to start doing them), you can set up permissions in such a way that actually forbids pushing merge commits to master, and that way ensure linear history.
- Releases are done similarly to in GitFlow. You create a new branch for the release, branching off at the point in master that you decide has all the necessary features. From then on new work, aimed for the next release, is pushed to master as always, and any necessary changes are pushed to the release branch (in my opinion, it’s an anti-pattern and a huge red flag if your release requires separate commits to work, but that’s a topic for another article – for simplicity, let’s assume you can’t or don’t want to change that). Finally, once the release is ready, you tag the top of the release branch. Then, because there is one eternal branch, there is only one way to get your release to be versioned permanently – and that is to merge the release branch into master and push that changed master. After that, all the changes that were made during the release are now part of master, and the release branch is deleted.
- Hotfixes are very similar to releases, except you don’t branch from an arbitrary commit on master, but from the release tag that you want to make the fix in. Again, work on master continues as always, and the necessary fixes are pushed to the hotfix branch. Once the fix is ready, the procedure is exactly the same as for a release – tag the top of the branch creating a new release, merge it into master, then delete the hotfix branch.