Git Workflow for teams

The size of the team determines how you use Git. Let’s define teams as small (up to 2 members), medium (5 to 9 members) and large (>9 members).

Each team member has a role of either Repository Contributor or Maintainer: Contributors only have read access to the repository. They are allowed to clone it but not commit to it. They contribute by submitting pull requests. Maintainers inherit the role of Contributors and additionally have write access, meaning they can commit. They are also responsible to integrate pull requests.

In small teams each member is a Maintainer with full read/write access to the repository. Ideally they do mutual code reviews.

In Midsized teams you have two or three Maintainers which are often the most senior members of the team. They review pull requests coming from Contributors.

Large teams of developers have many Maintainers which are assigned to repositories of sub projects within an overarching project. Contributors will feed their changes to those Maintainers via pull requests.

In some large teams (such as Linux development) a Benevolent Dictator Workflow is used: Contributors submit pull requests to Maintainers which in turn submit pull requests to the Dictator Repository.

Branching strategies

Master and Develop are commonly long running branches that remain open and receive regular merges. Feature branches are short lived branches created for a particular feature. They get merged in Developer branch. Hotfix branches are created off of the Master to quickly apply a patch to a production environment. They are then merged into other long running branches.

Git Development workflows

Trunk Based workflow

In a Trunk-Based development (TBD) workflow there is only one long running branch (the trunk) which is used for pulling, syncing and merging. Occasional Release branches are allowed. In general branching from trunk, such as when using a feature branch, is not acceptable. With few exceptions developers commit directly to trunk. The selling point behind TBD is that it reduces problems that would occur when merging long-lived branches, such as breaking the build, duplicate work and incompatible changes. To make TBD work you have to commit small commits on a daily basis. You have to organize your work in small tasks. The goal is to have trunk as close to release-ready as possible. Every time the build is broke, teams do anything to fix it. This workflow profits from a tightly integrated CI/CD infrastructure, which does not deploy broken builds or rolls back to a previous version automatically.

Git Flow

Git Flow workflow is based around releases using two long lived branches Master and Develop. Master is a copy of the current production code and is tightly controlled by Maintainers that only allow changes via Pull Requests. Develop is the parent branch of feature branches that should be short lived. When enough features are developed a Release branch is created. Bugfixing in Release can be done in parallel with new work being performed in Develop. Release is merged into Master after the code is sent to production. Finally Develop is synced with Release. Additionally Hotfix branches are created from Master to fix production bugs and are finally merged back into Master and Develop. Master and Develop must be access protected to only allow Maintainers to merge.

You can use standard Git commands but it is recommended to install GitFlow scripts that make working with GitFlow much simpler. After installation you run git init and git flow init. The latter installs git hooks and also gives you the chance to define custom branch names for GitFlow.

GitFlow Feature Branches

A Feature branch is where your work for a specific feature is done.

Why a feature branch?

Here are some reasons why we should not simply want all our work done on the main branch:

  • It is difficult to track your work on the main branch, separation from one feature to the other is difficult
  • Difficult to manage merges. Imaging you only want to have specific features in the main branch, then you would have to cherry pick your commits
  • It is difficult to back out: What if you find out half-way that your approach of implementing the feature was wrong? It is difficult if not impossible to undo all your changes that you already committed to the main branch
  • Without feature branches it is difficult to experiment with features

Starting and publishing a feature branch

A developer starts a feature with git flow feature start feature-name and commits changes as usual. To make the feature branch available on the remote repository the developer runs git flow feature publish feature-name.

Reviewing a feature branch

A reviewer could now run git flow feature track feature-name to pull the remote branch, track it locally and switch to it – all with one command. Reviewer can now commit changes and git push the changes back to the remote repository.

Finishing a feature branch

The developer can now git pull changes made by the reviewer. Once the feature is done the developer runs git flow feature finish feature-name which will merge the feature back into develop as well as check out develop. This command also deletes the feature branch locally and remotely! But it still exists locally for reviewer who has to delete it with git branch -d feature-name.

Creating and publishing a release

git flow release start release-name will create a release branch and check it out locally. To set up a tracking branch and pushing it remotely you run git flow release publish release-name.

Bugfixing a release

Developer can pull and check out the release with git flow release track 'release-name'. Bugfixes are committed and git push as usual. The release has to be merged into develop with git checkout develop and git merge release/release-name. You need to git push those local changes to the remote repository.

Finishing a release

Finally git flow release finish release-name will merge the changes into local master branch, tag the release, merge it back into local develop, delete the release locally and remotely and switch back to develop. Now git checkout master and git push --tags, because we also want to push the tags.

Creating a Hotfix

git flow hotfix start hotfix-name will create hotfix/hotfix-name based on master and switch to it. Developer commit hotfix and run git flow hotfix finish hotfix-name which will merge the hotfix into master, tag the hotfix, merge it back into develop, locally delete it and switch to branch develop. Now push those local changes of two branches to remote with git push --all origin.

About Author

Mathias Bothe Contact me

I am Mathias, born 38 years ago in Heidelberg, Germany. Today I am living in Munich and Stockholm. I am a passionate IT freelancer with more than 14 years experience in programming, especially in developing web based applications for companies that range from small startups to the big players out there. I am founder of bosy.com, creator of the security service platform BosyProtect© and initiator of several other software projects.