After struggling with and sorting out a workflow for web development with git, I've been tasked with adding in a staging server at the last second. We develop/test locally and push out to a repo, and now there needs to be a sandbox in between so people in other departments can play around and try out new things without breaking stuff.
Remote repo needs two long-running branches (in the spirit of nvie's branching model), master and develop.
We need to be able to push to one repo, and checkout the develop branch to test.site.com docroot, and when ready, merge develop into master and checkout master into site.com docroot
So on the server...
git init
git add .
git commit -m "Initial commit"
git checkout -b "develop"
And on our local machines...
git clone user@site.com:/repos/repo1.git
???
git push origin/develop (??? Updates test.site.com docroot)
And back to the server to make code live
git checkout "master"
git merge develop (??? Updates site.com docroot)
git checkout -b "develop"
And locally
git pull
Help with the question marks or alternative suggestions appreciated.
Edit:
Am experimenting with some of the answers so far. Had come up with a completely hacky idea in the interim and thought I'd share:
One post-receive hook to rule them all.
We clone a bare repo and track develop. Push develop to origin/develop.
Post-receive - Set GIT_WORK_TREE to test.site.com, checkout -f develop
If the commit message contains "merge_master", sets GIT_WORK_TREE to site.com docroot,
git checkout master
git merge develop
git checkout -f master (this would be for hotfixes)
Merge master back into develop and pull locally
After the dust settles, send email with difflog, wring your hands and have a sip of something strong.
How many different ways could that break?
In git, you normally never push to repository that has checkout. At all. You can push to one except you can't push to the checked out branch, but you usually shouldn't. Mainly to keep things clearly separated and to avoid disrupting the central repository if the web server breaks and needs to be fixed up.
So you should have a bare (term used for repository without work tree) central repository. Everybody will be using that repository. It should live on your internal network and should not be in docroot of either public or test server. In fact I would strongly suggest not putting it on either server at all, but separate (possibly virtual) machine.
Now you have the web servers. They can be either working copies, or just exports created using git archive
and can be updated either by cron, or post-update
hook in the central repository.
Working copy is updated by git fetch central-repo-url master
on site.com
server and by git pull central-repo-url develop
on test.site.com
server (ok, the fetch + reset suggested in the other answer is probably better, because you are sure you don't have any changes there).
Export is updated by getting a zip or tar pack with git archive
and extracting it. It's a bit more work.
Cron just runs the commands periodically. It's easier to set up, but the disadvantage is that there is a delay between push and the content appearing on the servers.
post-update
hook is a more complicated to set up, but does not have that delay (there is obviously some delay since downloading the data takes some time too). Basically you create a script to run the update, that can be triggered either by ssh or web request on the servers. Than in the central repository you put script hooks/post-update
, that will trigger the scripts on the servers. There should be no way to give them any parameters and the mechanism should not allow running any other code than those scripts for security reasons. You can make the hook more advanced by looking which branch was pushed (see git doc for where you find it) and triggering only the correct server.
Than git push origin develop
will cause the test.site.com
to be updated (but indirectly, via the scripts) and to make code live you'll do (on development machine; never work on server!)
git checkout master
git merge develop
git push origin master
and the last command will cause site.com
to be updated, again indirectly via the scripts.
My suggestion is to have both docroots be git working copies.
After this you have two options
- Have a cron job do something like
git fetch development-repo; git reset --hard development-repo/branch
for both. I've done stuff like this before, and the hard reset is a must: it'll nuke any random changes that have crept into your docroot somehow.
- Have a
post-update
hook in the development repo do the above every time somebody pushes something. Unfortunately I don't have a working example of this at hand.