Git Branching Strategy

Having been in two discussions about Git branching strategy with different organizations, I decide to jot down the details of all the common branching strategies in one post. This should help organizations develop their branching policies. There is a lot from Atlassian documentation. I also find this article a good resource.

Centralized workflow (no branching)

In Centralized Workflow, the team uses a central repository to serve as the single-point-of-entry for all changes to the project. The default branch is master, and all changes are committed to this branch. This workflow does not require any other branches beside master. Local changes may conflict with upstream commits, and conflict needs to be resolved.

This workflow is usually seen in teams transitioning from SVN, with very basic skill level. This workflow may also be adopted in teams working on configuration management instead of source code. Centralized workflow is great for small teams. The conflict resolution process can form a bottleneck as the team scales in size.

Feature Branch Workflow

Instead of directly committing on their local master branch, developers create a new branch every time they start work on a new feature. Feature branches should have descriptive names (e.g. issue#112). Feature branches are pushed to the central repository so that they can be shared to other developers without touching any official (master) code.

To get feedback on the new feature branch, create a pull request in a repository management solution (e.g. Bitbucket Cloud, Bitbucket Server). Before merge, you may have to resolve merge conflicts if others have made changes to the master branch of repo. This is to make sure your local master is synchronized with the upstream master. When your pull request is approved and conflict free, you can merge your branch to master branch.

Feature Branch Workflow: Merging a feature branch
feature branch workflow

The Git Feature Branch Workflow is a composable workflow that can be leveraged by other high-level Git workflows. Git Feature Branch Workflow is branching model focused, instead of release focused. The Git Feature Branch Workflow can be incorporated into other workflows. The Gitflow, and Git Forking Workflows traditionally use a Git Feature Branch Workflow in regards to their branching models.

Gitflow Workflow

First published in 2010 by Vincent Driessen. Gitflow defines a strict branching model designed around the project release. This provides a robust framework for managing larger projects. In addition to Feature Branch Workflow, Gitflow workflow assigns very specific roles to different branches and defines how and when they should interact.

Instead of a single master branch, this workflow uses two branches to record the history of the project. The master branch stores the official release history, and the develop branch serves as an integration branch for features. It is also convenient to tag all commits in the master branch with a version number.

This workflow is operated in the following ways:

  1. A develop branch is created from master
  2. Feature branches are created from develop. When a feature is complete, with PR reviewed, it is merged into the develop branch. Features branches
  3. Once develop has acquired enough features for a release (or a predetermined release date is approaching), we fork a release branch off of develop. Creating this branch starts the next release cycle, so new features can be added to develop after this point. On the release branch itself, only bug fixes, documentation generation, and other release-oriented tasks should go in this branch. Once ready to ship, the release branch gets merged into master and tagged with a version number. In addition, it should also be merged back into develop, which may have progressed since the release was initiated.
  4. Maintenance or hotfix branches are used to quicly patch production releases. Hotfix branches are a lot like release branchs and feature branches except they’re based on master instead of develop. As soon as the fix is complete, it should be merged into both master and develop (or the current release branch), and master should be tagged with an updated version number.
Git flow workflow - Hotfix Branches
Gitflow workflow

There is a Git extension named git-flow to provide high-level repository operations for this Workflow, such as start a release, finish a release, start a hotfix, finish a hotfix.

Gitflow is ideally suited for projects that have a scheduled release cycle and for the DevOps best practice of continuous delivery. It ensures that the master branch reflects what is deployed (e.g. in production). However, it is quite complex and have a steep learning curve for organizations. It also runs long-lived branches, which is considered bad from CI/CD perspective. Branches are by definition to isolate and hide changes, whereas continuous integration is about exposing changes early on and frequently. In that sense, the Gitflow branching model and CI/CD are mutually exclusive ideas.

In 2020, Vincent Driessen added a note at the beginning of his article on Gitflow:

If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team.
If, however, you are building software that is explicitly versioned, or if you need to support multiple versions of your software in the wild, then git-flow may still be as good of a fit to your team as it has been to people in the last 10 years.

Driessen’s notes also points to some simple alternatives. On the other hand, the Atlassian tutorial on Gitflow has described Gitflow workflow as a legacy (since Aug 2021 based on web archive). It points out at the beginning that Gitflow has fallen in popularity in favor of trunk-based workflows.

GitHub flow, Trunk-based development (TBD) and GitLab flow

It is operationally expensive to manage multiple mainlines in Git flow workflow, with both source control and release in the picture. Some simple alternatives have been evolved, with single mainline, for example, GitHub flow and trunk-based development. They differ in where the release is performed from.

In the GitHub flow, release is performed from branch before being merged back to master (trunk).

GitHub flow

In trunk-based development, release is not performed until the feature branch has been merged to the trunk (master). In trunk-based development, feature branches are supposed to be short-lived. It is a common practice among DevOps teams, since it streamlines merging and integration phases.

Trunk-based development

Trunk-based development has gained some momentum in recent years, due to its DevOps friendliness. This is a website that advocates it and here’s a DZone article about it.

Gitlab Flow

In response to the shortcomings of GitHub flow and Gitflow, Gitlab introduced its own proposal of branching strategy, known as Gitlab flow. The most distinctive aspect is the environment branches. In Gitlab flow, you run multiple long-lived branches, each of them representing an environment. The typical steps are as follows:

  1. You create short-lived feature branches, and merge them often to the master.
  2. Every developer starts from master and targets master. Other branches are merged from previous lower environment branches.
  3. You can deploy a new version to production, by merging master into the production branch.
  4. If you need to know what code is in production, you can check out the production branch to see.
Gitlab flow

You only need to work with release branches, if you need to release software code to the outside world. Here are some best practices in GitLab flow.

Forking Workflow

Forking workflow is fundamentally different. The key steps are as follows:

  1. A developer ‘forks’ an ‘official’ server-side repository. This creates their own server-side copy. This is their personal public repository, and no other developer are allowed to push to it.
  2. The new server-side copy is cloned to their local system. This forms an environment dedicated to this developer.
  3. With the local clone, developer needs to create the upstream remote manually using “git remote add upstream” command. This allows the developer keep the local repository up-to-date as the official project progresses.
  4. A new local feature branch is created. Developer commits to the new local branch, and pushes to their own copy of repository on server.
  5. Developer files a pull request from the new branch (in own copy of repository) to the ‘official’ repository. The project maintainer knows that an update is ready to be integrated. The PR also serves as a discussion thread.
  6. The PR gets approved for merge and is merged into the original server-side repository.

This workflow has other names, such as fork-and-branch workflow, and is commonly used in GitHub for managing open-source projects. However, this should not be confused with the aforementioned GitHub flow.

Conclusion

In order to fully support distributed source control, Git abstract version control problems into concepts such as commit, branch, etc. This makes discussion about Git workflow and branching strategy difficult due to the conceptual hurdles and organization differences. We covered choices of Git branching strategy in this post. As Vincent Driessen commented in his original Gitflow posting, panaceas don’t exist. We should consider the context (e.g. team size, Git skill level, etc) to determine the best Git branching strategy.