Branches: master, production, staging. New features branched from production, merged into master, merged into staging. Deploy staging. When accepted, merge from feature branch into production. Deploy production.


Wednesday was a productive day. I was rattling through the to-do list on daPulse, flipping responsibilities from red (developer to do) to purple (customer to check). It looked like a good day on the git log, too – I was getting stuff done. The GoToMeeting went well as we ran through each feature on the staging website with the customer. Until: “Good job Ian, now can you put live those features the customer’s signed off?”

I had developed features X, Y and Z but I’d only gotten sign off for X and Z.

Well, for this kind of job I would just get a list of files I’d changed that day with a git diff --name-only firstSHA..HEAD and SFTP them up*, but that wasn’t going to cut it. Different commits related to different features and they weren’t always in order – for example, if I’d tweaked a feature later on. Just one commit after another into master.

Right, so I’m going to need some organisation. I head off to refresh my memory on git-flow and it’s good but it doesn’t fit this scenario. It fits if I’m building up a release in an organised manner where I know what’s going in now and what’s going in in the next iteration. But I don’t. Every feature needs to hang around until it’s ready to be plucked into production.

The method

We create two new branches: staging and production. These will contain exactly what’s on each environment’s server. When we start a new feature we do so by branching a new feature branch from the tip of production. We do that because we have a stable known starting point when we eventually deploy it, with no dependencies that haven’t yet made it to production.

When we think our feature’s ready, we merge it into master. master represents the collection of all features we’re working on in development. We might commit small changes here, but anything we do commit here will need to be cherry picked into production. Wherever possible we prefer a feature branch off of production.

Commits from master can be:

  • Merged into staging, or
  • Cherry picked (not merged) into production.

When we’re ready to show the customer we merge master into staging and then deploy it immediately so it’s on the staging server ready for customer acceptance.

Here’s a summary:

git checkout production  
git checkout -b my-new-feature  
# time passes  
git commit # maybe a few times  
git checkout master  
git merge my-new-feature --no-ff
# time passes  
git checkout staging  
git merge master --no-ff
# deploy

When the feature is signed off, the feature branch should be merged into production, and deployment should be made to production.

git checkout production
git merge my-new-feature --no-ff
# deploy


We’ll keep some sanity and have faith in deployments if the branches labeled staging and environment accurately reflect what’s been deployed to those environments.

  • When deploying to production: only publish from the production branch
  • When deploying to staging: only publish from the staging branch
  • Deploy immediately after updating either branch

To keep things tidy, delete feature branches after they have been merged into the production branch. git will let you delete branches without warning so long as you’ve merged them into an existing branch but you will want to keep your features around at least until you’ve merged them into the production branch.

I prefer --no-ff to see the history of each feature. It adds a lot of clarity that can get lost in a fast-forward commit.

What might this look like?

Here’s a screenshot of a contrived workflow through developing two features. In this scenario the customer only signs off on feature 2. Note how the .gitignore commit is cherry-picked into production whilst the features are merged.

Staging and production website git workflow


I am just putting this into practice for the project. I wonder about unpublished features depending upon each other and how that will impact this workflow. Ideally each feature will be self-contained, even if that means a bit of repeat code in multiple features that will be resolved (DRYed up) in a later merge.


  1. I prefer automated deployment whenever possible