A successful Git branching model

Author: Nvie (Vincent Driessen)

Time: 2010-01-05

Reflection notes

Update: the 2020-03-05

The branching model (git-flow) presented in this article was conceived in 2010, shortly after the birth of Git, a decade later. Over the course of this decade, the model became so prominent in so many software teams that people began to see it as a standard — and, unfortunately, more as a dogma or a panacea.

During this decade, Git has taken over the world, and the most popular types of software developed with Git are more likely to be Web applications. Web applications, on the other hand, are typically delivered continuously, not rolled back, and you don’t need to support multiple versions running simultaneously in the marketplace.

This is not the kind of software I had in mind when I wrote this blog post a decade ago. If your team is working on continuous delivery of software, I recommend using a simpler workflow (such as GitHub Flow) rather than trying to shoestring Git-Flow into your team.

However, if you are building explicitly versioned software, or need to support multiple versions of software in the market, then git-flow may still be a good fit for your team, just as everyone has been using it for the past decade. In that case, read on.

Above all, it is important to remember that there is no panacea. You should get what you need, and the remedy should suit the case.

introduce

In this article, I’ll introduce a development model that I started introducing about a year ago for some work and personal projects, and it’s proven to be very successful. I’ve been working on this article for a while, but haven’t found the time to put pen to paper until now. I won’t go into the details of any project here, just focus on branching strategy and release management.

Why Git

For a full discussion of the pros and cons of Git versus centralized source control, see this page, where a lot of heated debate is going on. As a developer, I love Git more than any other tool right now. Git has really changed the way developers think about merging and branching. In traditional CVS or Subversion, merging or branching is a scary, once-in-a-while operation.

But with Git, these operations are so simple that they are actually a core part of your daily workflow. For example, in a CVS or Subversion book, branching and merging are discussed only in the sections for advanced users, whereas in a Git book, it is covered in the sections that introduce the basics.

Because of its simplicity and frequency of use, branching and merging is no longer a fear. A version control tool should be better suited than other tools to assist with branching and merging.

So much for the tools, let’s move on to the development model. The model I’ve introduced here is essentially nothing more than a set of processes that each team member is required to follow in order to make the development process manageable.

Decentralized but centralized

The warehouse configuration we are using that works well with this branch model requires a “true” central warehouse. Please note that this repository is only considered a central repository (since Git is a distributed version control system, there is no such thing as a central repository on a technical level). We call this repository Origin, because all Git users are familiar with the name.

Every developer pulls and pushes from Origin. But in addition to this centralized push and pull, each developer can also pull code changes from others to form a subteam. For example, when two or more developers collaborate on an important new feature, they can avoid pushing code to Origin prematurely. In the figure above, there are subteams of Alice and Bob, Alice and David, and Clair and David.

Technically, this simply means that Alice has defined a Git remote named Bob that points to Bob’s repository, and vice versa.

The main branch

In essence, the development model is largely inspired by existing models. The central repository contains two main branches with an infinite life cycle:

  • master
  • develop

Every Git user should be familiar with the master branch in Origin. There is also a Develop branch in parallel with the master branch.

We think of Origin/Master as a master branch whose HEAD source always maintains a production-ready state.

We think of Origin/Develop as a master branch whose HEAD source always reflects the latest development changes delivered for the next release. Some call it the “integrated branch.” This is the source of the code that automatically deploys Nightly Builds.

When the source in the Develop branch is in a stable state and ready for release, all code changes should somehow be merged back into the master and then marked with a release number. The details will be discussed later.

Therefore, every time a code change is merged back into the master, a new production version is defined. This should be strictly restricted, so in theory you can use Git Hook scripts to automatically build the software and deploy it to the production server whenever the master commits.

Auxiliary branch

In addition to the main branch, Master and Develop, our development model uses a variety of auxiliary branches to facilitate collaborative development among team members, simplify tracking of features, prepare for production releases, and facilitate quick resolution of online releases. Unlike the main branch, these branches will always have a limited lifetime, as they will eventually be deleted.

These special branch types include:

  • Feature branch
  • The Release branch
  • Hotfix division

Each of these branches has a clear purpose and is bound by strict rules about which branches may be its original branch and which branches must be the target branch to which it will merge. We’ll get into that in a minute.

From a technical perspective, these branches are not “special.” These branch types are actually classified according to how we use them. Of course, it’s still just a normal Git branch.

Function branch

Possible original branches:

develop

The branches to which you must eventually merge:

develop

Branch naming conventions:

Any name other than master, develop, release-*, hotfix-*

The feature branch (or sometimes called the topic branch) develops new features for upcoming or future releases. When you first start developing a new feature, you may not know the target version to which the feature will be incorporated. The nature of feature branching is that it exists as long as a feature is under development, but eventually it will either be merged back into Develop (to make sure new features are added to upcoming releases) or dropped (not happy with them).

Feature branches usually exist only in the developer’s repository, not in Origin.

Create a branch of functionality

When you start developing a new feature, check it out from the Develop branch first.

$ git checkout -b myfeature develop
Switched to a new branch 'myfeature'

Merge the completed functionality into Develop

You can incorporate the completed features into the Develop branch to ensure that they are added to upcoming releases.

$ git checkout develop Switched to branch 'develop' $ git merge --no-ff myfeature Updating ea1b82a.. 05e9557 (Summary of changes) $ git branch -d myfeature Deleted branch myfeature (was 05e9557). $ git push origin develop

The –no-ff parameter ensures that a merge operation always creates a new commit object, even if the merge operation can be fast-forward. This avoids the loss of history information for this branch of functionality and groups all commits for that functionality together. For comparison:

In the latter case, you can’t see from the Git history which commits are implementing a feature together — you have to read all the log information yourself. The latter can be a headache if you want to restore an entire function (a group of commits), which is easy to do with the –no-ff parameter.

Yes, this creates some (empty) commits, but the benefits far outweigh the costs.

Distribution branch

Possible original branches:

develop

The branches to which you must eventually merge:

Develop and master

Branch naming conventions:

Release – *

The distribution branch is used to prepare for the new production release. It allows for last-minute detail changes. In addition, it allows for subtle bug fixes and preparing metadata for the release (version number, build date, and so on). By doing this on the distribution branch, the Develop branch will be able to easily accept the functional requirements of the next major release.

An important time to check out a new release branch from the Develop branch is when Develop has (almost) reached the desired state required for the new release. At a minimum, all functionality for the release to be built must have been incorporated. All future functionality that is intended for release must wait until the distribution branch is created before merging.

Creating a distribution branch begins by assigning a version number to the upcoming release. Until now, the Develop branch reflects changes to the “next release,” but it’s not clear whether the “next release” will eventually be called 0.3 or 1.0 until the release branch is created. This decision is made based on the project version number rule at the time the release branch is created.

Create a distribution branch

The distribution branch is created from the Develop branch. For example, the current production release is 1.1.5, and we are about to release a large release. The Develop branch is ready for the “next release” and we’ve decided to upgrade it to 1.2 (rather than 1.1.6 or 2.0). So we check out the distribution branch and give it a name that reflects the new version number:

$git checkout -b release-1.2 develop Switched to a new branch "release-1.2" $./bump-version.sh 1.2 Files modified successfully, Just as from the front door of the cab as from the front door to 1.2. $git commit-a-m "just as from the front door to 1.2" [release- 1.274D9424] just as from the front door to 1.2 1 files changed, 1 insertions(+), 1 deletions(-)

After we create and switch to the new branch, we change the version number. The bump-version.sh here is a fictitious shell script that changes some files in the working environment to reflect the new version number. (You can also change it manually — this is just to say that some files will be changed.) Then the files with the modified version number are committed.

This new branch may exist for some time until it is certain to release it. In the meantime, some bug fixes may be made on this branch instead of the Develop branch. It is strictly prohibited to add large new features here; they must be merged into the Develop branch, so you need to wait for the next big release.

Complete a distribution branch

When the state of the distribution branch is ready to become a true distribution, a few things need to be done. First, merge the distribution branch into the master (remember this, because every new commit on the master is a new release that we defined). Next, you must tag this version of history to make it easier to refer to it later. Finally, you need to incorporate the changes you made on the distribution branch into Develop so that future releases include these bug fixes as well.

The first two steps:

$git checkout master Switched to branch 'master' $git merge --no-ff release-1.2 merge made by recursive. (Summary of $git tag-a $git tag-a $git tag-a

This version has been completed and tagged for future reference.

update: It can also be used
-s
-u <key>Parameter to encrypt the signature of the label.

To retain the changes we made in the distribution branch, we need to merge them back into Develop:

$git checkout develop Switched to branch 'develop' $git merge --no-ff release-1.2 merge made by recursive. (Summary of  changes)

This step is likely to cause merge conflicts (which are highly likely due to the version number change). If so, please fix and submit.

Now that we are done, we can remove the distribution branch because we no longer need it:

$git branch -d release-1.2 Deleted branch release-1.2 (WAS FF452FE)

Thermal repair branch

Possible original branches:

The master

The branches to which you must eventually merge:

Develop and master

Branch naming conventions:

Hotfix – *

The hot fix branch is very similar to the release branch in that, although it is unplanned, its purpose is to prepare for a new production release. They are actions taken in response to a poor state of the production environment. When a defect in the production environment must be fixed immediately, the hot-fix branch can be created based on the label on the master branch corresponding to the online version.

The essence is that the team member’s work (on Develop) can continue while the other person prepares the production environment for a quick fix.

Create a hot repair branch

The hot repair branch is created from the master branch. For example, version 1.2 is a production version that is currently running online and has some bugs due to serious bugs. But the code in the Develop branch is not yet stable. We might pick up a hot fix branch and start fixing the problem:

$git checkout -b hotfix-1.2.1 master Switched to a new branch "hotfix-1.2.1" $./bump version.sh 1.2.1 Files modified successfully, Just as from the front door of the cab as from the front door to 1.2.1. $git commit-a-m "just as from the front door to 1.2.1" [hotfix- 1.1.41e61bb] Number to 1.2.1 1 files changed, 1 insertions(+), 1 deletions(-)

Don’t forget to increase the version number after checking out.

Then, fix the bug and commit the fix in one or more commits.

$git commit -m "Fixed severe production problem" [hotfix-1.2.1abbe5d6] Fixed severe production problem 5 files changed, 32 insertions(+), 17 deletions(-)

Complete a hot repair branch

Once completed, the hot fix needs to be merged back into master and back into Develop to ensure that the fix also exists in the next release. This is very similar to how the distribution branch is done.

First, update the master and tag the distribution.

$git checkout master Switched to branch 'master' $git merge --no-ff hotfix-1.2.1 merge made by recursive. (Summary of $git tag-a 1.2.1 $git tag-a 1.2.1

update: It can also be used
-s
-u <key>Parameters to encrypt the signature to the tag.

Then, incorporate this fix into Develop as well:

$git checkout develop Switched to branch 'develop' $git merge --no-ff hotfix-1.2.1 merge made by recursive. (Summary of changes)

One exception to this rule is that when there is a distribution branch, you need to merge the hot-fix changes into that distribution branch instead of Develop. When you incorporate hot fixes into the release branch, you will eventually incorporate hot fixes into Develop when the release branch is complete. (If immediate development needs to fix this bug, and you can’t wait for the release branch to complete, it’s safe to incorporate the hot fix into Develop.)

Finally, delete the temporary branch:

$git branch -d hotfix-1.2.1 Deleted branch hotfix-1.2.1 (WAS ABBE5D6)

conclusion

Although there is nothing really shocking new about this branching model, the “big picture” at the beginning of this article has proved very useful in our project. It forms an elegant mental model that is easy to understand and allows team members to form a consensus on the branching and publishing process.

A high-quality PDF version of the diagram is also available here. Hang it on the wall for quick reference at any time.

update: If anyone needs it, here’s a copy of the main image
Keynote source file.