It seems to be quite the common theme to have this three-tier workflow. Here's how we've done it. We're a Ruby shop, so there's some mention of testing here too.
We all work on individual "stories" (from Pivotal Tracker) separately from each other. This means that if we were all committing to the master branch, then we'd be stepping on each other's toes constantly. To stop this problem, each of us creates a new branch (based off the latest master) for that specific chunk of work.
When we complete that chunk of work, we run the tests ourselves and if they're passing then they get merged back to the master branch where the tests are ran again to ensure that there's no breakages that have been introduced. If there have been, we try using git bisect
to figure out what it was, and that works in 99% of cases.
Most of the time (because we're really awesome*), the tests pass. When all the tests are passing on the master branch then we deploy to our staging server. So I guess this means that master is the staging branch. When that feature (or more likely, features) have been given approval, then we merge those changes into a production branch and then push that branch up to the production site.
With this setup, the individual developers are all able to have a runnable copy of the application for themselves, the QA team gets a running copy to go over when they have the time (this is the "most fun" part of my job, gathering people is like herding cats) and the Real World has a perfect site.
In theory, anyway. People make mistakes.