A (successful) git branching model

*This blog post tells about how we improved a VCS workflow to another one that suited our and the consumer needs. It was a great result: we minimized the chances of occurring one of the worst problems for a developer in a project: big integration while we maintained an ‘almost releasable branch’ all the time

In the last months we’ve been working on a project with a mixed development team (Plataformatec’s team and the customer’s team). We, of course, used a version control system (specifically git) and we set up a nice git branching model for our team. As agilists, we know that we should not use anything that requires a lot of bureaucracy (things like opening a ticket to integrate a branch into the trunk).

Using nvie guide as base, we developed a git workflow. First of all, we had three main branches:

  • production: contains the code that is currently on production. We also have a production server that contains, obviously, the production code up and running.
  • staging: contains the code that is being tested before going to production (we used this branch to deploy to a production-like environment that worked as a final test until production, this environment is also called staging)
  • master: contains the already accepted features. To consider a feature as “accepted” we deployed to another environment (called “dev”) and asked a QA analyst to test it. Once approved we merged the commits. This “dev” environment is used for this kind of approval and also for general purposes like when we say: “take a look at this new awesome feature we’re developing”.

For each feature that we developed, we created a git branch (almost all of them we pushed it to the remote server to facilitate code review and to deploy to the “dev” environment). Everyday, we ran git rebase master, to update our branch code (except for features developed by more than one developer). Once the feature is complete, we rebased master into it, and merge it using –no-ff (to create a merge commit). For the branches that more than one developer worked on, we usually talked and set up a “rebase period” where one does the rebase, forces the push (because you changed your local tree so git does not accept it as a non forced push) and updates the remote branch.

Close to the production deploy, we merged (always using –no-ff) master branch into staging branch and deploy to staging. Once approved, we merged to production, and created a tag telling the current version of the application and then we do the deploy. When we deployed to production we also removed the merged branches from the remote repository.

One of the great advantages of this schema is: Master is always “almost” ready for a release. Yeah, some features really deserves to be validated right before the deploy, because another feature can break them, but we kept the master as an “always releasable stable” branch (and also we used a continuous integration tool in order to enforce all tests passing). Another great advantage is: as we updated our code everyday, it was very unusual for us to face big integration scenarios.

For the “dev” environment deploy we also set up a capistrano task that asks which branch we want to deploy to be possible to deploy something from any branch.

This workflow has worked really well for us and maybe it is useful to you (maybe for you to adapt it to something that works better for you as we did with the workflow suggested on nvie).

To summarize, this is our git workflow in commands (supposing that we are on master branch):

git checkout -b my-awesome-feature

(... you do some code and some commits and you go home to have some sleep or maybe play some starcraft 2 ...)

#(arrived at the office on the next day)
git rebase master

#(... continue working and commiting and sleeping (or maybe playing some starcraft 2 ...)
git rebase master

# (... some commits ... and voila ... you've finished...)
git push origin my-awesome-feature
cap dev deploy

# (...YAY! QA analyst just approved it ...)
git pull origin master
git rebase master

# (run the tests to ensure all of them pass)
git checkout master
git pull --rebase origin master
git merge --no-ff my-awesome-branch
git push origin master

#(... it is time to validate on staging)
git checkout staging
git pull origin staging
git merge --no-ff master
git push origin staging
cap staging deploy

#(... QA analysts validate the staging ...)
git checkout production
git merge --no-ff staging
git tag -a v1.4.2 -m "Releasing on 13th February"
git push origin production
git push --tags origin production
cap production deploy

Well, this is how we improved a git workflow based in another one. As almost everything, there is no bullet proof for it, but we found interesting to share this experience with you as it was a success (every developer on the team enjoyed it). But please, we would like to receive some feedback about it :-). Have you used something similar in your team? Do you have any ideas on how we can improve it?

9 responses to “A (successful) git branching model”

  1. Timur Vafin says:

    Why just do not use gitflow?

  2. What is your battle.net login? 😉

  3. Anonymous says:

    rlmflores, I play on the NA Region 🙂

  4. Anonymous says:

    I didn’t know about it. Seems great, I will for sure try to use it.

  5. Timur Vafin says:

    Yep, it’s very cool, give it a try! )

  6. Anonymous says:

    Hey guys, could you paste the relevant Capfile part that allows to specify what branch to deploy from (that you’re using for dev env)? Thanks

  7. Hi @eugenebolshakov:disqus 

    Use this:https://github.com/capistrano/capistrano/wiki/Frequently-Asked-Questions/37b57ba10997cdcd540a9e68f68c097a329b2ba0 

    Of course, change the Capistrano variable in ask from :user to :branch. 

  8. Hi @eugenebolshakov:disqus 

    Use this:https://github.com/capistrano/capistrano/wiki/Frequently-Asked-Questions/37b57ba10997cdcd540a9e68f68c097a329b2ba0 

    Of course, change the Capistrano variable in ask from :user to :branch. 

  9. Anonymous says:

    Thanks, I ended up with this in my config:

    set :branch, exists?(:branch) ? branch : ‘master’

    This allows me to specify the branch I want to deploy from in the command line like this:

    cap deploy -s branch=my_branch

    But also provides a default (master) if I don’t specify anything