Real-life git-flow

The concepts of git-flow are elegant and simple, but the examples show only one repository. Using git-flow on a team is a little more complex. This post endeavors to describe the setup process and branching considerations when git-flow is used in a team environment.

If you’re the person who wants to bring git-flow into your team, this post is for you. Everyone else on the team needs these concepts of git-flow.

Scenario: the team has been developing on a master branch for a while, and now that we’ve made our first release, it’s time to implement git-flow. Each of our local repositories track origin, which is a bare repo living on a server. So far, origin has just one master branch.

Converting an existing repository to git-flow

For the basics, everyone needs a develop branch, origin needs a develop branch, and our develop branches need to track origin.

To set up git-flow, one person runs this:
git flow init
git push -u origin develop
git branch –set-upstream develop origin/develop

Note: if you have any branches lying around other than master, git flow init will not create the develop branch for you; it wants me to use one of these others. Either delete these branches or create the develop branch manually (git branch develop) before initializing git-flow.

git push -u origin develop creates the develop branch on origin. The “-u” option means “set-upstream,” which modifies your develop branch to track the develop branch on origin. After this, git status will tell you when your branch is ahead of origin, behind origin, or both.
Everyone else on the team runs this:
git flow init

Note: if these other guys have a branch lying around other than master, git flow will refuse to create the develop branch. Either delete these branches or create the develop branch manually (git branch develop origin/develop) before initializing git flow. The “origin/develop” at the end of that command sets up the tracking branch.

Multiple Branches + Multiple Team Members

Now that origin has two branches, and each team member is tracking both of them, there are a few other considerations.


If you’re used to using git pull, please stop. Please use git fetch. This downloads all the new stuff from origin without modifying your local branches or working directory. Decide which local branches you want to update; when you’re ready, rebase.

What used to be “git pull” becomes:

Fetch the updates from origin.

Observe whether we have changes.

Rebase to tack my changes on the end of the changes from origin. 

Why rebase instead of merge? That’s a subject for a whole post (or a presentation! come see me at DevLINK), but the short answer is: non-fast-forward merge creates merge bubbles on the develop branch – evidence that development forked and a merge commit with no story to tell. The git-flow model is cleaner if merges are for features. This is my opinion.


I recommend this magic spell:

git config push.default current

Run this once to tell git push that it should (by default) push only the current branch. This way, when you make commits on develop and push them to origin, you won’t be confused by error messages that occur because your master branch is behind. 

Sharing branches

Feature and release branches exist only in the repository of the team member who created them. Chances are good you’ll want multiple programmers working on these at some point – for instance, when everyone is fixing bugs found in test on the release branch. There’s a detailed post on sharing branches by GitGuys, but I’ll give you the quick rundown here.
One person starts the release and pushes it to origin:
git flow release start versionName
git flow release publish versionName
Whoever else wants to work on it does this to set up a tracking branch:
git flow release track versionName
Bonus hint: here’s how to list branches on origin you’re currently tracking:
git remote show origin
Now everyone can push changes to the release branch. When it’s complete, one person finishes the release. 
  1. make sure master, develop, and the release branch are all fully up-to-date in your local, because finishing the release will affect all three of these. 
  2. git flow release finish versionName
  3. push both master and develop branches
  4. push the release tag to origin: git push origin versionName
  5. delete the release branch on origin: git push origin –delete release/versionName
Finally, everyone else should delete their release branch.
git branch -d release/versionName


If nothing else, always remember to push the release tag to origin. Anyone looking for that version of the code is going to look for it there. git push origin versionName
Like anything in git, git-flow is easy until it isn’t. Remember that you have the full power of git at your disposal; the git-flow commands are only shortcuts. Wrap your head around the commit graph, know what’s going on behind the scenes, and keep your chin up. Easy right?

5 thoughts on “Real-life git-flow

  1. so the publish word is the key. Now I'm trying to figure out how to see remote changes. Right know I'm doing git fetch & git status to see the changes. There's a way to do only git status and see remotes changes!?Thanks for the tips to working with a team.

  2. I'm new to git, but if I understand your question correctly, you'd like to see how your local branch diverges from it's tracking branch on remote without a fetch?I use this: git diff –stat origin/develop

  3. I would like to see a real-life git flow where release maintenance is factor in. A real-life case would be a re-release is not at the tip of development, but a hot-fix/fix from the release point.I understand the release can be created from the tags, but as soon as first 'fix' is needed for the release, then the release branch is going to need to be pushed to server. Something like libc or python managing version 2.7 and 2.8. A bug comes into 2.7, you aren't grabbing the tip of development, you are grabbing the last 2.7 release, fixing, and re-release 2.7 (bump version 2.7.x+1, tag …) AND I think pushing that release branch to server.Correct?

  4. Talk about late to the party…Try the –dry-run switch on fetch. It'll show you what it would do if you were actually fetch but not actually update any of your local pointers.

Comments are closed.