Home » Blog » Deploy WordPress theme code to your server with git push

Deploy WordPress theme code to your server with git push

Here’s the setup: You’re a good little developer and you’re building your latest WordPress Theme using git to track your changes. You’ve got a local environment, where you develop and test edits to your theme file but you don’t have a good way to push those changes to the server. Yesterday, I was doing my development using git but relying on FTP to send updated files to the server. Today, I simply use git push. Here’s how: 

First, I’d like to thank Philip Brown for this tutorial, which got me started, and this GIST by noelboss, which also helped.


I assume you know a bit about git, aren’t confused by the term SSH, and can work on the command line. What we’ll be doing here is setting up a remote repository on your server, setting it as a remote on your local git project, and configuring a git “hook” on the server to move files to a particular directory on your server when you use git push.

When we will be done, your workflow will look like this:

git commit -m "latest changes"
git push dev-server master

Then your remote server will have the latest changes! Here are the steps:

  • Create a folder on your server where you’ll keep “bare” git repositories
  • Init a “bare” repository for your project
  • Configure the post-receive git hook to send the latest files to the deployment directory

For the purpose of this example let’s assume the project I’m working on is called “goodgoodproject” and it’s a WordPress theme. I have a local development environment that I’ll refer to as “local” and a server that I’ll refer to as “remote”.

If you run into problems, I’ve added a “gotcha” section at the bottom.

Set up the remote server

The first thing you’ll need to do is get SSH access to your server. Once you have that, I would recommend setting up your ~/.ssh/config. It saves so much time and you can read more about that here. I’ll use normal syntax throughout the tutorial though.

Connect to your server. It should look something like this:

ssh username@hostname.com

If you use a port, that command might look like this:

ssh -p 22000 username@hostname.com

Once you’re on your server, create a folder in your home directory where your git repositories will live. I called my folder git/, but you could call it anything. We’ll make an empty directory using the mkdir (make directory) command.

mkdir git

Now we want to make a repository for the “goodgoodproject”. Let’s change directories using cd (change directory) command.

cd git

This leaves us in our new folder at ~/git/. Next, we will use the git init command to initialize a new git repository, but pay attention to what I do here:

git init --bare goodgoodproject.git

I’m initializing a “bare” repository, which means that there aren’t working files, but instead just the bare essentials that make up a git repo. I’ve also added goodgoodproject.git to the end, which tells git to initialize a bare repository in a directory of that name. I use the dot git ending just so I can indicate that this directory contains a git repo, but you can names yours anything.

Create your deployment directory

This is the directory (on your remote server) where you want your code to end up. In our example, I’m working on a WordPress theme called goodgoodproject. I want it to be in the wp-content/themes/ folder in my web directory on my remote server. Your server might be different, so be aware of where you want to write your files! My public HTML file is where I’ve installed WordPress so, to create my deployment directory, I’m going to use the make directory command like so:

mkdir ~/public_html/wp-content/themes/goodgoodproject

Now there’s an empty folder where the files from my git repo will be placed when I deploy them from my local machine!

Setup the post-receive hook

Git has these “hooks” which allow you to perform special actions based on actions taken by or upon the git repo. The post-receive hook is activated when git push is done on the repo. This will become clearer as we go on.

First, let’s change directory to the hooks folder of our new repository on our remote server.

cd ~/git/goodgoodproject/.git/hooks

We are going to create a file in this directory called “post-receive” and put some content in it. To do this, we simply have to open the file with a text editor from the command line. I like using nano but you could use vim or anything else. nano is the program on my server (and most UNIX servers) that allows me to edit text, so we’ll use this command:

nano post-receive

We are now in the nano program. It should be mostly black, no text on the inside. We are going to write this simple little sh script. I’ve added comments after # hashtags; they aren’t read by the script, but they’re there for your reference. Except for the first line, the #!/bin/sh is called a hashbang; it tells the computer how to run the following file:

# the target is the deployment directory we created earlier 
# the GIT_DIR is the directory where our git repo we made is
# this is the command we want to run when this hook is fired
git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f
# finally anything we echo will show up as a message on our local machine when we push to our server!
echo "latest files moved to $TARGET"

A quick note: The $HOME variable is the same as ~; it expands to the home directory of the user. The above code is for a sh script, or shell script. This means that when the post-receive hook files, we are simply running a shell script on the server. Thanks to this, you could do loads of cool stuff when you push to the repo. Right now, we’re simply running the following command:

git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f

This is using the git checkout -f (force checkout) to put files from the git-dir HEAD into the work-tree path. I’ve used the variable $TARGET and $GIT_DIR to make the code a bit more readable and easy to edit.

Nano editor

Now, we save our file. With nano, you will press Ctrl+X and it will ask you if you want to write. Press y then press return. Finally, we need to make sure that our new file can be executed! We’re going to run the chmod or Change Mode command on our new file and set it so that it can be executed.

chmod +x post-receive

That’s it. Now, we’re ready to go back to our local environment.

Setup the local git repo

I’ll assume you’ve already gotten your local git repo set up, so just navigate to that folder. We’re going to add a remote repository! From our git repo on our local machine, we’re adding a remote named server. You can, however, name it whatever makes sense to you!

git remote add server ssh://username@hostname.com:22000/git/goodgoodproject.git

This looks a bit complicated, so let’s break it down. The first part is a normal git command to add a remote repo. You’ve probably done this with a service like github.com; that’s the git remote add server part.

Let’s look now at the remote url we’re using. It starts with ssh:// this simply tells git to use SSH as the protocol for connecting to the server. Next comes the username. This is whatever your SSH account’s user name is. Then we have @hostname.com, typically this is the server’s web address, but it might also be the IP address. Followed by :22000, this is the port. Sometimes you need a port, sometimes you don’t     I just wanted to make sure that you know what it looked like. After that, we have /git/goodgoodproject.git, which you should recognize this as the path to the git folder you initialized earlier!

Because I use an ssh config alias, my remote url is much easier:

git remote add server goodgoodwork:git/goodgoodproject.git

Just remember that you need a colon between the SSH address and the path to your remote git repo.

Great! Now, the local repo has a new remote called server. All you’ve got to do now is push to it!

git push server master

This will push the master branch to the server url. Once it lands on the server a program called git-receive-pack will trip the post-receive hook and fire the shell script to add the files to our deployment directory.


There are a few places where things could go awry, so here’s a look at them:

git-receive-pack: command not found

You do your git push from the local machine and get the following error:

bash: git-receive-pack: command not found
fatal: Could not read from remote repository.

This means that the git-receive-pack command couldn’t be found on the remote server. I use Namecheap hosting (and so should you), which for whatever reason does not add this command to the PATH by default. It provides a simple fix, where you add the folder where third-party programs live on their server to your PATH file via .bashrc file. I won’t go into that, but you should be able to figure out a solution to your environment based on this.

You need to test if git push works

When I was setting this up, I kept running into the problem where I’d do a git push, something wouldn’t work, I’d fiddle around on the server then want to test again but, when I did, another git push I’d get would be:

“Everything up-to-date”

There was an easy way to get around this: just add a dummpy commit:

git commit --allow-empty -m 'push to execute post-receive'

It adds useless commits to your history, but who’s paying that any mind?


I hope you found this helpful if you learn of any more gotcha’s I’ll add them. I would love to hear how other people use this and other hooks.

Oh yeah! I also wrote a little bash script that you can put on your server to generate these git repos with ease.


  1. mindpixel says:

    This is great! Just a quick question though. Is this copying ALL files into the directory every time you run git checkout or is it only copying the files that changed? I ask because every time I do a push, all the files within my app show a new modified date and not just the files that changed like if you do a git pull.

    • drew says:

      I’m not entirely sure, but I believe it is replacing the whole thing. Let’s break it down… We’ve got this line that runs in the post-receive file: git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f

      That’s what runs when you push to the server. So the docs say that -f is force and is “used to throw away local changes”. Meaning that it will use code from “theirs” or the code coming in if there is a conflict.

      --git-dir points to the directory where our repository is while --work-tree points to where we want to code to go. The work tree is, as I understand it, a different directory where you keep a “tree” or collection of git “branches”. So if I’m understanding the inner workings here, each time we push to remote and execute the checkout command we are force creating a new “tree” on top of our existing files.

      I still feel a bit confused by this honestly 😛

      • mindpixel says:

        Thanks for the reply! Yeah I’m just trying to make sure I understand properly to avoid any major downtime on critical files etc when pushing updates. Also if it’s rewriting the entire directory each time I would have concerns about system resources that are being wasted such as the I/O on the server. Eager to learn more!

  2. Joefrey says:

    I follow your steps. But when I push my changes ‘git push server master’

    git push server master
    fatal: ‘/git/themeleft.git’ does not appear to be a git repository
    fatal: Could not read from remote repository.

    Please make sure you have the correct access rights
    and the repository exists.

    Please help..

    • drew says:

      Sure thing! Looks like your remote isn’t pointing to a server but to a non-existant file on your computer. Review the setup local git repo section of the post and make sure you followed along with that.

      My guess is that you added a remote to /git/themeleft.git rather than the full server address which would look something like this ssh://username@hostname.com:22000/git/goodgoodproject.git.

      First go to the repo in shell and type git remote -v this will print out your remotes. On your “server” remote see if there is the ssh:// protocol or any username@hostname business. If not then that’s your issue.

      You are building the remote “location” much like you would write out an ssh connection. For example I might connect to this server using an ssh command in shell like: ssh drew@goodgoodwork.io for my git remote repo location I would write that same command like this: git remote add server ssh://drew@goodgoodwork.io I would of course need to point to a specific location on my server, that’s where the colon and file structure comes in. Adding your remote should look something like this:

      git remote add server ssh://joe@themeleft.com:~/git/themeleft.git The colon is basically saying “on this server go to this location”. You should be able to use the tilde (~) to indicate the home directory of the user you are connecting to the server with, generally the home directory is where all the action happens on shared server environments, for instance it’s where most servers place the pulic_html folder with your web accessible files (like wordpress).

      Hope that helps.

  3. Chris says:

    Hi Drew

    Thanks for the article! I get the same as Joefry:

    fatal: ‘/git/mysite.git’ does not appear to be a git repository
    fatal: Could not read from remote repository.

    Please make sure you have the correct access rights
    and the repository exists.

    I ran git remote -v and get
    server ssh://myuser@ (fetch)
    server ssh://myuser@ (push)
    (I’ve changed IP & port details above)

    I can SSH in to the server successfully so I must have permission right?

    If I SSH in & navigate to git/mysite.git how can I confirm there that git is setup correctly?

Leave a Reply

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