Simple and safe live code deploy using Git


This post is going to describe how I use git for code deploy and updates to plugin code to my live WordPress sites.

Simple way – use git pull

Obviously, the simplest way to deploy with git is to make the live plugin a repository and just run git pull in the directory and update the code.

The trouble is that this will mean the live version of the code will include the .git directory and a full history of all the branches and changes made. It will also mean that those directories will need to be masked from the web server or they become a target for hackers.

Another downside of using a simple pull command is that there may be some processes which need to be run when the code is deployed, like for example some css pre-processing. You don’t really want to be running git pull and then some other processes to produce the final version of the code on the live server every time you make a change. The ideal deploy process would be a simple, single command which can be run to do a complete deploy.

Using git push for code deploy

So the process I’m going to describe here is a slightly more complex process, but it makes it easy and quick to deploy your code changes with a simple git push.

An obvious assumptions here is that you use git for revision control. If you don’t use it, or don’t use revision control at all then you can probably stop reading here!

The first part of the process is to set up a new ‘bare’ repository for your code base on your live server. You also need to set up a directory where the checked out code is going to live. On my server I’ve used /usr/local/sites as the home for all my sites so that was an obvious choice for these directories as well.

cd /usr/local/sites
sudo mkdir deploy/myplugin.git -p
sudo mkdir deployed/myplugin -p
sudo chown simon:www-data deploy -R
sudo chown simon:www-data deployed -R

The directories can be called anything you want, I use deploy.git for the repository and deployed for the checked out code. I also set the permissions to be owned by my user and the group set to www-data although all you need to do is make sure that the user you use has ssh access to the server.

In the repository directory you need to initiate a ‘bare’ repository which is a repository which contains all the branch and history information, but doesn’t contain any of the actual code.

cd /usr/local/sites/deploy/myplugin.git
git init --bare

The next step is to add a hook into this new repository which will be run when a git push is received. You do this with your favourite editor by adding a script called post-receive in the .git/hooks directory.

cd /usr/local/sites/deploy/myplugin.git
vim hooks/post-receive

In this script we add the following

git --work-tree=/usr/local/sites/deployed/myplugin --git-dir=/usr/local/sites/deploy/myplugin.git checkout -f

That may appear wrapped on your screen, but it’s all one line. Essentially, what it means is when the repository receives a push command, the files are checked out into the deployed directory. The interesting point is that when the files are checked out, there is no .git file in the resulting directory. It’s not a repository, just a copy of the files.

Of course we could check the files out into the final destination directory where we want the plugin to live on the live server, but the advantage of checking them into a temporary store is that we can run any other process which needs to happen before they are moved into place.

The other piece of the puzzle is to add this bare repository as a remote destination on the machine where you do your development.

On my mac I use a vagrant ubuntu virtual machine for development, so I would move to the plugin development directory and add a remote with:

git remote add live ssh://[email protected]/usr/local/sites/deploy/myplugin.git

This adds a new remote destination for the project. In order to deploy the code, once I’ve made sure everything is correct and I want to push it out the the server I just run:

git push live master

This pushes the latest changes to the remote repository, where the post-receive hook runs and checks the code out into the deployed directory for the project.

Processing the plugin files

So the next part of the process I add is to include a processing script in the project which is used to complete any processing that the particular plugin I’m working on needs.

I add a bin directory to my project and include in that directory a script which carries out any actions required, and also copies the plugin files to the final destination on the server.

In order to get the script called, I add an additional line the the post-receive hook which was added above. So the full contents becomes:

git --work-tree=/usr/local/sites/deployed/myplugin --git-dir=/usr/local/sites/deploy/myplugin.git checkout -f
cd /usr/local/sites/deployed/myplugin && /bin/sh bin/

This means that once the code is checked out, the deploy script for that particular project is run, and that script can be tailored to suit the project you are working on as part of the normal development process.

Typically, in my projects I will make the final action an rsync to copy the files to their final destination and I include a file of contents I don’t want copied, so a typical script might be:


sudo -u www-data rsync -avz --exclude-from 'bin/exclude.txt' $SOURCE $DESTINATION 

In the exclude.txt file I will include the bin directory and any editor config files etc, so typically it might be:


The other point to note about this is that I use ‘sudo -u www-data’ so that the there are no permission problems when the files are moved into place on the webserver. You would need to add an entry in the /etc/sudoers file for this to work without a password.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

As you found this post useful...

Please consider sharing on social media!

Leave a Reply

Your email address will not be published. Required fields are marked *

Big changes coming to the Photoblog Previous post Big Changes coming to the Photoblog
An Advanced Custom Fields form showing the Conditional Logic selector Next post A Useful Advanced Custom Fields Conditional Logic trick.