In this lesson, we’ll focus on workflow. More specifically, we’ll use the helpful GitHub service hooks to automatically update a project on our personal server whenever we push updates to a GitHub repo.
Prefer a Video Tutorial?
Step 1 - Create a Git Repo
We certainly need some sort of project to play around with, right? Let’s do that right now. Using which ever tool you prefer (I’d recommend Structurer), create a new directory, called awesomeProject, and add an index.html file. Feel free to populate this with some gibberish markup for the time being.
With our test directory in place, let’s create our first Git commit.
If you’re unfamiliar with Git, I highly recommend that you first review “Easy Version Control with Git.”
Open the command line:
cd path/to/awesomeProject git init git add . git commit -m 'First commit'
Those familiar with Git should feel right at home. We’re creating a Git repo, adding all the files to the staging area, and are then creating our first commit.
Step 2 - Uploading to GitHub
The next step is to upload our project to GitHub. That way, we can easily call a git pull to download this project from any computer/server we wish.
Again, if you’re not familiar with GitHub, and haven’t yet created an account, read Terminal, Git, and GitHub for the Rest of Us.
Begin by creating a new Git repository.
Next, you’ll need to fill in some details about your project. That’s simple:
And finally, since we’re already working with an existing Git repo, we only need to run:
git remote add origin git@github.com:Your-Username/awesomeProject.git git push -u origin master
With that out of the way, our awesomeProject is now available on GitHub. That was easy!
Step 3 - SSH
Now, we certainly need some sort of live preview for our project, ideally stored on our own server. But this can be a pain sometimes. Push your updates to GitHub, login to your server, manually transfer the updated directory, etc. Granted, this only takes a moment or so, but when you make multiple changes through out the day, this can quickly become a burden.
But one step at a time. We’ll tackle this dilemma in Step 4. For now, let’s simply pull in our Git repo to our server. To do so, we need to SSH in.
Depending upon your host, your SSH credentials will vary slightly. Search Google for “your-host-name SSH,” and you’ll surely find the necessary instructions. Once you’re ready, let’s move along:
We’ll use my personal server as an example:
ssh jeffrey-way.com@jeffrey-way.com <enter your host password>
And with those two lines, we’re in!
Next, we cd to the parent directory of where we wish to store awesomeProject. For me, this will be: cd domains/demo.jeffrey-way.com/html/. Of course, modify this according to your own directory structure.
Git Clone
Let’s clone the GitHub repo now.
git clone git@github.com:Your-User-Name/awesomeProject.git
Give that command a few seconds, but, before you know it, that directory is now available on your server, and, in my case, could be viewed at: http://demo.jeffrey-way.com/awesomeProject.
Step 4 - Creating a Connection
The inherent problem at this point is that there’s no specific connection between our GitHub repo and the directory stored on our server — at least not an automated connection. For example, if we update our source files on our local machine, and then push the changes to GitHub:
git add index.html git commit -m 'Added photo of dancing chicken' git push origin master
These changes will certainly not be reflected on our server. Of course they won’t! In order to do so, we must – once again – SSH into our server, cd to the awesomeProject directory, and perform another git pull to bring in the updated source files.
Wouldn’t it be great if, every time we pushed updates to GitHub, those new source files were automatically updated on our live preview server?
As it turns out, we can do this quite easily with GitHub service hooks.
You can access this page by pressing the “Admin” button from within your GitHub repo, and then clicking “Service Hooks.” The “Post-Receive URL” option will instruct GitHub to send a POST request to the specified page every time you push to your GitHub repo. This is exactly what we need!
“We’ll hit these URLs with POST requests when you push to us, passing along information about the push.”
To make this work, we’ll need to create one more file that will handle the process of performing the git pull. Add a new file, called github.php (or anything you wish – preferably more vague), and add:
<?php `git pull`;
So now you’re thinking: “Jeff’s gone crazy. You can’t put a Bash script into a PHP string.” Well…yes you can, once you realize that those aren’t single quotes above, they’re back-ticks.
When you wrap a sequence in back-ticks, in PHP, it’ll be treated as a Bash script. In fact, it’s identical to using the bash_exec function.
Save that file, and upload it to the awesomeProject directory on your server. When finished, copy the url to that file, and paste it into the “Post-Receive URL” textbox. In my case, the url would be http://demo.jeffrey-way.com/awesomeProject/github.php.
With this in place, every single time you push to your GitHub repo, that file will be called, and the awesomeProject directory on your server will auto-update, without you needing to move a finger. Pretty nifty, ay?
You Also Might Enjoy:
- Getting the Hang of GitHub
- Terminal, Git, and GitHub for the Rest of Us
- Easy Version Control with Git
- Getting Good with Git – eBook

This was what i was exactly looking for.
Had a little struggle with setting ssh keys, but it is working perfectly.
Thanks for such a awesome post.
I’m having trouble with my SSH keys through Dreamhost. I’ve followed all tutorials and debugs on github site as well as an exhaustive Google and StackOverflow. Anyone else have similar issues and find a fix?
Very nice. Is something like this possible on BitBucket?
Sorry – not sure.
If you’re referring to the post-receive url then yes. You can find that in Bitbucket in your repo menu under Admin->Services
Yes it is possible. I fought through it for a couple days, but finally figured it out. Here’s how I implemented Mercurial/BitBucket and SSH with Netbeans IDE. The post will probably still help even if you aren’t using Netbeans.
but at the start, you still have to pull your repo manually to your server right? and that’s also assuming that hosting provider have git installed.
Yup.
I’d really be interested if there was a way to accomplish this without having Git installed on the remote server.
If you’re with mediatemple, they have git installed by default on all their servers. I’m sure you have seen their hosting deals before, I would definitely recommend it.
Curiously enough, github has SVN read-only support..
https://github.com/blog/626-announcing-svn-support
So you can still pull from a github repo if you only have SVN on the server.
the service hook trick is awesome
Minor detail but in your video you are doing a ‘git add index.html’ every time you change the file. You only need to do this for files that are not already tracked by git.
Like the way you use terminal in full screen like that must google it.
lastly I wondering is there any potential security flaws having a github.php file that runs on the command line call. I can’t think of any but its always a dangerous thing. also what user is doing the command e.g. is it www-data and can that lead to issues in git if I’m usually setting up the git with my own user e.g. ‘jefferyway’?
Thanks for the service hook tip, very nice!
However, I have set up my Git SSH key with a passphrase and every time I do a ‘git pull’ it asks for a passphrase to the private keyfile. Apparently GitHub service hook doesn’t carry this passphrase in the POST request and thus fails.
Any possibility to fix this?
ElCynico, you need to use ssh-agent (or System Keychain if you’re using OSX Snow Leopard). You can find out all about it here: http://help.github.com/working-with-key-passphrases/
For the server git pull, i would recommend to pull from the HTTP URL. In the repo view, you can change the URL to the ‘http’ view instead of ‘ssh’ and you wont need credentials (or any of the passkey setup).. The only downside is you can’t push from your server (which is probably okay).
I think you could just use ssh-agent.
On my server every time I run a GIT command it asks me to enter in my passphrase. Does anyone know if there is a way to disable this?
My friend helped me. Turns out I just needed to recreate my public ssh key and leave the passphrase blank. Either that or in my shell script use except (http://en.wikipedia.org/wiki/Expect)
How would you do this same thing but have certain branches push to different servers? For example have all the pushes into “master” go to the production server while everything else go to the development server.
Thanks!
I wonder exactly the same. I wish someone here answered that question.
Fullscreen terminal: Visor http://visor.binaryage.com/
For SVN people: you can accomplish ALL of this using Beanstalk http://www.beanstalkapp.com
Very nice video. I will definitly try this in my next projects.
I don’t know what you use to get the terminal fullscreen, but it inspired me to set the the terminal in a keyboard maestro application shortcut and in a full screen opacity to get the same effect :).
Jeffery, you’re a god among men. I’ve been looking for something like this for a long time. I recently began messing around with the Cloud 9 IDE, and wasn’t sure how I would push my changes to my server (it doesn’t support FTP yet).
Not only does this solve my Cloud 9 question, but it also removes steps when working locally.
thank you thank you thank you.
Trust me — the Gods are *way* better than me. :) But yeah – SSH knowledge is a good tool to have.
I am a big fan of SSH and GitHub. Really nice tutorial. Good Work Jeffrey. Looking forward to your other tutorials.
Thanks.
Sorry, i watched the video and still dont understand the benefits of github, when i could just as easily work with my localhost make a quick backup and then upload the changes via ftp with filezilla…
Is there something im missing, is this really beneficial?
You won’t be able to track changes between each backup without downloading the backup, unzipping everything and then using a program to compare the files.
Plus, writing 3 lines in terminal > downloading, zipping and uploading a bunch of files.
Nice trick, but why I can’t file such script only if master/origin branch is updated…
Ofcourse I can use PHP to get $_POST data and decide what to do with such commit, but why fire script, if proper branch isn’t updated? ;)
But I thing that this is more suggestion for github staff, not for You ;)
Great tutorial Jeffrey =)
Very nice, i definitely want more real-world workflow tuts
Excellent as always, Jeffrey, but I think there is an even easier way to do this, especially if you are on a closed project, such as for a client.
Springloops.com has been around for a while using SVN, but with their new 2.0 release (which I am currently using on a few projects) Git is supported. You can easily setup multiple servers, have collaboration, and have Springloops automatically push files to the server without having to create the extra ‘github.php’ POST file on your own server. It rocks!
Interesting – checking it out now.
Uhm.. it seems that using github is kinda useless if you have a server wich has git on it.. with some more configuration you could set up Gitosis or some other git wrapper (http://wiki.dreamhost.com/Gitosis).
For me this is the perfect solution, (no 3rd party), although github has it’s advantages..
Maybe – but for those like me who use GitHub on a regular basis, it makes sense.
Excellent stuff, thanks for this.
So question… how secure is this? What prevent’s a person from just visiting github.php via URL?
Right – this would probably be used for much smaller projects, like demos and such. And then, you should also make the github.php file a unique name so that it’s not easily accessed.
You could prevent the pull from running unless the file is being accessed from GitHub.
Restricting should be easy in Apache .htaccess. Maybe you could limit file access to POST requests only.
Something like this?
Order allow,deny
deny from all
Also, unique filename for the script helps a lot, though it has to be updated manually every now and then to GitHub.
You could always pass a key to the page, example.com/git.php?key=your_salted_string and just kill the script if the key is not correct.
I haven’t read this yet. But judging by the title, this is the article I’ve been waiting on.
Thank you jeffrey. You rock my world.
Is it be possible to push to a personal server without the GitHub account?
This is the closest info I’ve found? http://toroid.org/ams/git-website-howto
(Trying to figure out security issues and remove the reliance on the third party account if possible)
Thanks interesting artcle, i shall give it a go
Backtick – A.K.A. the “grave” for those of us who learned english in the era of 8-track tapes and record albums.
The timing of this tut is really … CREEPY! :-) Just been changing my workflow to git this week.
I think you should really include in the tutorial that your web-host must have git installed. I tried to do this, however to push changes to my web-host directly instead of passing to github but my web-host doesn’t have git installed! *sigh*
There is a way to install git if your web-host doesn’t have git installed. Download git source files upload to you home directory and ‘make’ then ‘make-install’ ALL in your home directory by specifying and optional install directory using –opt-directory=”blah-blah-blah”.
BUT, the flaw is that gcc must be installed to to able to compile the source in the first place :-) Sorry.
For those concerned about security, you can skip the service hook. Add your web-host directory as another remote location but that will need you to push to it whenever you want the changes to be reflected. If like Jeffrey you use github also. Then that would need you to push to github THEN your webhost.
For those who don’t understand why Jeffrey finds this method useful … its because, with this method you just need to push ONCE to github and your site just pulls from github. Two birds with one stone kind of methodology. And also simply because github ROCKS. Somethings just don’t have a reasonable explanation to be honest.
Hi,
Thank you for this nice & simple introduction. I have been wanting to use an easy version control system.
Jeff… sorry, but I don’t see anywhere else to communicate with Nettuts on a general, non-article topic except for comments. I just want to thank Nettuts+ for NOT participating in the BAIT AND SWITCH scam today over premium discount coupons. [If you did initially, you have at least taken down the post after the apparently ONE coupon was used.] Over the years, many of us (well at least I) have begun to trust your recommendations and comments. It’s nice to know that, even though earning and maintaining trust doesn’t seem to apply to other Envato editors, your integrity is still intact. [Besides, with over 81,000 registered followers and countless thousands un-registered who needs a sham?] Thanks… and I’m sure you’ll be glad not to have to endure the firestorm of negative comments that is occurring over on the other sites.
And, oh, no pressure, just wondering when the “cheerful website” post you promised for 80,000 followers might be posted?
Hey DJ –
Thanks, but it certainly wasn’t a scam — more a clerical error that has since been fixed. :) Envato is about as honest company as you can get.
Jeff… Yes, I agree with your assesment – at least that’s the image that I have gotten from about every thing I’ve seen over the past three years. I’ve watched how you personally handled issues and even how Nettuts+ (and Envato) handled all the furor in the comments back when promotion of premium content began being promoted on “free” pages and readers were upset at titles not clearly reflecting the “sales” nature of the post. You openly took care of it and titles now clearly don’t set any unclear expectations.
The high ethical expectations we have of Envato is what, I’m sure, is fueling all the “unrest” in the comments on other Envato sites; otherwise, we would merely erase the blogs from our bookmarks and move on. Obviously we don’t want to; cause, really, where would we go?
If you don’t have ssh access on the server (read: cheaper server space), you might need to install René Moser’s git-ftp (https://github.com/resmo/git-ftp) to use git to upload files. You won’t be able to use github’s service hooks this way though :( Requires two pushes.
Unless I’m wrong – in which case I’d love to be corrected.
Awesomesauce. I’ve been meaning to find a way to “auto pull” to my live server when pushing from local. You rock.
Neat trick with the service hook. I’ve never seen that before. It’s a shame most cheap hosts don’t have git installed.
I would highly recommend also integrating Capistrano into this setup. Git + Guthub + Capistrano lets you deploy with 2 or 3 commands. They also have a gem for a “rails-less” deploy, for non rails projects, which I use for nearly all mine.
Argggh why this not working!!
i can pull from my project directory on the server, but when i try to run the php bash script nothing happens :-/
We have a similar setup at MetaZaku that Yorick described above using Git, Gitolite, Redmine, and multiple servers. Instead of pushing updates to a URL, we configured the repository to SSH into remote servers with a ‘git-pull’ SSH trigger. Similar but not the same.
The biggest difference between our setup and this system is that we use Redmine to manage the issues, wiki, and code browsing. It’s like a personal GitHub for your team or company.
Excellent tutorial man!
@jeffrey, thanks for the article, its great. Got everything running except for the auto pull using the backtick command.
Pretty sure its something I need to configure back at mediatemple gs server… but not sure what or how.
I create a .profile file for using an ssh-agent but still if i do the git pull manually thru shell its still asking me for passphrase so I assume the github.php file runs into the same issue and does not do the pull automagically.
thanks for any insight :)
Really nice idea but perhaps I’m being a doofus here. If you’re triggering the git pull from a PHP file served by Apache, will the git pull not be run as the Apache user (www-data or like)? In which case does that user need SSH keys etc.?
Basically, if I hit github.php in a browser, I get nothing. If I run the github.php from the command like, “php github.php”, it works…
It might be because I’ve trying to do this against a private repo that I’m having so many problems with it.
Any ideas?
I’m having the same issues. Apache user cant exec a bash script but the terminal user can. Can anyone explain a way around this issue?
Ditto here. Trying to figure out how to:
- Enable the proper permissions on the local repo for the Apache user on Mac server
- Setup SSH key for the Apache user to successfully git pull from a private repository
Finally figured it out (perhaps in somewhat insecure fashion).
- Had to eventually assign ownership of the entire repo folder to apache user (www on my Mac server)
- sudo -u www -s to test git in terminal as apache user… created a .ssh folder in /Library/Webserver and went through the process of setting up the ssh key pair for www and adding it to GitHub
- also had to specify full path to git in php page ‘/opt/local/bin/git pull’ (or you can update the path for the www user)
A little bit complicated, can’t solve problem that Apache user cant exec a bash script too:(
Great article. I am in the process of moving all our website and api source to GitHub.
My question is about workflow with dynamic sites like blogs or web apps that also create content on the web server on the fly.
Lets say I have a domain which has our website, blog and also some api’s which are used by our mobile app. Ideally i want that domain as one repo. (I had thought about one repo for the all domains/subdomains but probably not a good idea).
So I start off with my local repo on my mac, I push that to GitHub and using a hook that gets pulled by the web server. So far this all makes sense. But what happens if content is created dynamically on the web server? (we have an api that creates some user files on the server). Is there a way to send those new files back to GitHub?
What about a WP blog where a number of editors may be uploading images etc. Every time the server pulls from GitHub would i lose those new files?
Sorry if these are silly questions. I am used to using SVN in Xcode for our app but so far all my web development work is done in Coda with a local copy on my Mac and I just sync to the server copy. You know, the usual way. So git is somewhat new to our workflow.
This tutorial needs a “sticky” to stay forever in frontpage.
This looks perfect but I have been trying to get his to work for the past two days without any luck
I can execute the file from command line (# php github.php) without it asking for a passkey otherwise nothing happens. I have the project in home/www/ Any ideas?
Hey,
Nice tutorial!
When I try and run git pull from php, I get a ‘Permission denied’. Is this because the command is being run by the ‘apache’ user and not by ‘root’ who created the keys?
If so, what is the solution?
Thank you.
Thanks for the great tut. This was exactly what I needed. Works great with Unfuddle btw.
Things I learned along the way (Probably obvious but wan’t for me) :
1. SSH user needed to be the primary user for my domain account on the server.
2. This same user needed to set the SSH keys and run git init.
3. Needed to turn off PHP Safe Mode so the script could run shell_exec.
Simple I am sure, but completely forgot to look at the safe mode setting. It was a new sever setup and I failed to check php.ini.
Thanks again.
This doesn’t (easily) work on shared hosting.
I’ve got a Github account setup with USERNAME1, and my shared hosting account through Mac Highway was setup with USERNAME2. IE: All of my files are owned on the server by USERNAME2—the name assigned to me by my host.
I really want this to work, but I don’t see a way around the two usernames.
Any one solved this issue? I suspect that many of us here are on shared hosting.
Thanks!
Terry
How to proceed when I do not have terminal?
Hi Jeffery!
Thanks for breaking down github and SSH. The tutorial was very easy to read and follow, and it made for an excellent first step into the world of social coding.
One puzzle:
I got everything working, except for the last bit of Step 4. I can’t get github to trigger the pull.php file. When I click on it manually (by launching the URL) the pull request works, and my server is updated from the repo. But for some reason, github won’t trigger it automatically when I commit & sync.
Any ideas?
Thank you for writing this tutorial. Up to this point I was referencing:
http://joemaller.com/990/a-web-focused-git-workflow/
http://webxl.net/2011/03/10/managing-wordpress-with-git/
This tutorial seems the most straightforward and allows integration with github which is fantastic.
The website I was wanting to use this for is is a WordPress based implementation, so changes would be happening on server as well as on my own computer. To accomplish that I made the github.php script look like this:
<?php
`git pull`;
`git add -A`;
`git commit -m "Update: Website based changes"`;
`git push`;
Now everything is kept in sync. Coupling this with Cloud9 I can use any computer to edit my code anywhere. I welcome any thoughts on my addition. Thanks again Jeffrey.
Great artice, it should be fasten on a main page;-)
Thanks Jeffrey, this is a brilliant tutorial.
Hi!
Nice article, I liked it a lot and put in use quite immediately.
Anyhow I need to report that my hosting provider contacted e to strongly recommend not running shell scripts or commands from inside a PHP script.
Second I would notice that on my hosting the `git pull` that pulls the git repo,
reports progress and messages to stderr so them are showing up in the error log.
Do you have any suggestion for avoiding this, without any third parties services and without double “push”?
Got this :
“Host key verification failed.
fatal: The remote end hung up unexpectedly”
Somebody knows how to fix ?