Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Simple automated GIT Deployment using Hooks

Simple automated GIT Deployment using GIT Hooks

Here are the simple steps needed to create a deployment from your lokal GIT repository to a server based on this in-depth tutorial.

How it works

You are developing in a working-copy on your local machine, lets say on the master branch. Most of the time, people would push code to a remote server like or and pull or export it to a production server. Or you use a service like my to act upon a Web-Hook that's triggered that service.

But here, we add a "bare" git repository that we create on the production server and pusblish our branch (f.e. master) directly to that server. This repository acts upon the push event using a 'git-hook' to move the files into a deployment directory on your server. No need for a midle man.

This creates a scenario where there is no middle man, high security with encrypted communication (using ssh keys, only authorized people get access to the server) and high flexibility tue to the use of .sh scripts for the deployment.


  1. Know how to use GIT, Terminal etc.
  2. Have a local working-working copy ready
  3. Have SSH access to your server using private/public key


  1. Create a folder to deploy to on production server (i.e. your httpds folder)
  2. Add a bare repository on the productions server
  3. Add the post-receive hook script to the bare repository (and make it executable)
  4. Add the remote-repository resided on the production server to your local repository
  5. Push to the production server, relax.

1. Have a local working-working copy ready

Nuf said. I asume we are working on master – but you could work on any branch.

2. Create a folder to deploy to

ssh into your prodctionserver:

$ ssh
$ mkdir ~/deploy-folder

3. Add a bare repository on the productions server

Now we create a "bare" repository – one that does not contain the working copy files. It basicaly is the content of the .git repository folder in a normal working copy. Name it whatever you like, you can also ommit the .git part from project.git or leave it to create the repository in an exisiting empty folder:

$ git init --bare ~/project.git

4. Add the post-receive hook

This scrtipt is executed when the push from the local machine has been completed and moves the files into place. It recides in project.git/hooks/ and is named 'post-receive'. You can use vim to edit and create it. The script does check if the correct branch is pushed (not deploying a develop branch for example) and

while read oldrev newrev ref
    # only checking out the master (or whatever branch you would like to deploy)
    if [[ $ref =~ .*/master$ ]];
        echo "Master ref received.  Deploying master branch to production..."
        git --work-tree=/home/webuser/deploy-folder/ --git-dir=/home/webuser/project.git/ checkout -f
        echo "Ref $ref successfully received.  Doing nothing: only the master branch may be deployed on this server."

Download post-receive for an improved version

5. Add remote-repository localy

Now we add the this bare repository to your local system as a remote. Where "production" is the name you want to give the remote. This also be called "staging" or "live" or "test" etc if you want to deploy to a different system or multiple systems.

$ cd ~/path/to/working-copy/
$ git remote add production

Make sure "project.git" coresponds to the name you gave in step 3. If you are using Tower or a similar App, you will see the newly added remote in your sidebar under "Remotes" (make sure it's not collapsed).

6. Push to the production server

Now you can push the master branch to the production server:

$ git push production master

If you are using tower, you can drag&drop the master branch onto the new production remote. That's it. Have questions, improvements?

while read oldrev newrev ref
# only checking out the master (or whatever branch you would like to deploy)
if [[ $ref = refs/heads/$BRANCH ]];
echo "Ref $ref received. Deploying ${BRANCH} branch to production..."
git --work-tree=$TRAGET --git-dir=$GIT_DIR checkout -f
echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."

this really help me a lot! thanks

Arigato gozaimasu

Jossnaz commented Feb 19, 2017

so if the server disappears or you delete it, you lose your git repository...

what about staging or test environment? different git repository? this makes no sense

Something tells me that TRAGET It must be replaced by TARGET

chadmiller commented Feb 27, 2017

set -eu


while read oldrev newrev ref
        # only checking out the master (or whatever branch you would like to deploy)
        if [[ $ref = refs/heads/"$BRANCH" ]];
                echo "Ref $ref received. Deploying ${BRANCH} branch to production..."
                git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f
                echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."

vqiu commented Apr 15, 2017

It's very helpful to me. Thanks.

ramseylove commented May 3, 2017

I did all this including making the post-receive script executable(chmod +x post-receive), but after I do a git push, no files show up in the target location. The git push looks successful. I am using command line so i didn't see the script responses but when I do a git remote status, or try to push again, git says that everything is up to date. What could be happening here? I tried this twice on two different hosting services.

Hi, It's very helpful, but I am facing an issue. I am using CWP web panel in my VPS. I am using git under my company domain. All things are working except, when I do a push, the files goes under development directory. But it showing "Internal Server Error", if I fix that using "Fix permission" tools in CWP admin, I am able to browse the URLs. But, post-receive hook permissions also changed and it's not working. Again if I add executable permission in post-receive files and push new update, the "Internal server error" starts showing. Please help

orome commented May 26, 2017

@ramseylove: This doesn't nothing for me either. Shouldn't it be checkout -f ${BRANCH}?

Hello guys i made this auto deployment application which you can use

Thanks for this great gist. I have though but one question. How can we setup this auto-deploy and checkout all the files as other user. I want to be able to checkout all the files and have the owner of the files to be www-data:www-data right now doing this way sets the user and group to whichever user create the post-receive hook.

Hello, Guru,
I tried one of your hooks -- post-receive.
I found that it works only I use git over SSH, however it will not work if I use git over HTTP.
Do you know this? or Could you please give me any advice?

Thanks in advance.

jopark94 commented Oct 2, 2017

I have similar problem with @zhengfish .
I'm trying to use it over HTTP, but it doesn't seem to work.
Are there any solutions to this?

ianmin2 commented Oct 6, 2017

This is the most to the point guide on this subject that I have come across.

This is about the tenth time i'm using it and it always gets the job done fast!

I am grateful for your sharing

Nanod10 commented Oct 17, 2017

I have a question, what happens if I replace a directory structure of this style

/ home / username /
     [public files not included in repo]
            [app files]
         [core files]

That's my directory structure ...
In my repository I do not have public_html files ...
I only have the directory structures (without all the other public_html files) and the app_folder and app_core files ...

I did not quite understand if using the Hook ...
1- What would happen in this case?
2- would lose the files of the public folder?
3 - would the files be combined?
4- What happens if I delete files from the repository? these will be eliminated?

I await your answers, tomorrow I will make some tests about it.

Thank you very much!!

--------------------- EDIT ---------------------

Well ... according to my tests:

  • files that match the output of the repository will be silently replaced.
  • Deleted files from the repository will be deleted from the output directory.
  • all other existing files not included will not be modified.
  • Folders are combined, if added files are added.
  • Folders removed from the repository, which exist on the server, are not deleted if they include files that are not included in the repository.
  • If the folder exists (as it is in the repository and the server) and is deleted, when they perform the push it will be completely eliminated.

There were some doubts that I had before using this directly in production since it was not clarified that is what happened explicitly.

Thank you very much for the post! The truth ... this is great! =)

cookra commented Oct 22, 2017


"so if the server disappears or you delete it, you lose your git repository...what about staging or test environment? different git repository? this makes no sense"

This is for a push to prod or any env you like..? Staging, testing a little old skool ;)

step 5....what is supposed to reference?

mydudechris commented Nov 22, 2017

Yeah, I don't understand this line either:

git --work-tree=/home/webuser/deploy-folder/ --git-dir=/home/webuser/project.git/ checkout -f

Should it be something like git --work-tree=/var/www/html/ --git-dir=/

What if you have configured your DO to use ssh keys with no password?

Also, you mention running this as an executable but you don't say anything about making it one. Does it run as a service, how does that work?

Khamba commented Nov 29, 2017

To get this work in git bash for windows I had to run this command:

eval $(ssh-agent -s) && ssh-add "C:\Users\YOURNAMEHERE\.ssh\key_name"

where key_name is the OpenSSH key (not the ppk key that putty exports by default)

Hope this helps some poor soul stuck with windows.

adnanh commented Nov 30, 2017

One could also use webhook server and GitHub's outgoing webhooks to automate this.

isaacalves commented Dec 18, 2017

I want to deploy my app's build folder contents to a folder on a Digital Ocean droplet.
I set up the post_receive hook and it works, but the files are never up-to-date.

For instance, if I do this:

  1. On the remote server, go to target folder (on which I want to checkout my build folder) and delete everything. List folder to make sure there's nothing there.
  2. Locally commit and push to production (which will trigger the post-receive hook on the remote)
  3. On the remote server, go to target folder to check the contents. The files are there, which means the post-receive hook worked.

But the files will be still outdated! They are an older version, I believe. Is this a caching issue? I was suppose to see this file main.9533e87b.css in a /static/css folder but I see this one: main.164fa07d.css .

If I run git log on my bare repo I can see the latest commits there. The build folder is in the repo. I'm pushing to origin (bitbucket) and I can see the correct file there. So how come the files that get checked out in the target folder and are out to date?

How can this possibly be?

A possible error while following the above process is the permission for file post-receive. The default creation of file has the permission read-write, but to work properly need to have also execute permion to do that execute the following command.
chom 777 $home/project.git/hooks/post-receive

dantuck commented Jan 31, 2018

I would love to use this gist but I am having trouble setting something up on my productions windows webservers that allows me to push my code too. I am bound to windows unfortunately but was looking for a lightweight option with low impact to a production server that would set up a remote for the bare git repos. Any options?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment