How to Setup “push-to-git” Deployment with SSH


Some things in life are very scary when you first approach them, but once you face your fears and tackle the difficulty before you, those things become less daunting. For some, learning how to setup “push-to-git” deployment is a lot like taking a shower with the guys in a high school gym class setting. You appreciate how showers benefit the stink of hard work, and you have no problems with showers within the jurisdiction of your own home but in a public arena, why?! I’m sure you already have ways of taking care of your own deployment business, and life is comfortable just as it is. Unfortunately there are times where we are simply avoiding the uncomfortable because of internal fear. Well fear no more! Deployment is a part of development that is part of growing up. How confident a developer feels about deployment is a sign of their maturation as a developer. So if you feel uncomfortable about diving into this topic, that’s natural, I assure you I’ve been there and done that and plan to make it as simple of a transition as possible.

What is “push-to-git” deployment?

Push-to-Git deployment is an alternative to the slow, clunky, FTP solution that most of us start with when migrating and launching code, files, etc. Git deployment is better first of all because “its Git,” meaning if you’re already using git for version control, now you will simply push your code to production or a staging/development environment. This tutorial does assume that you are already familiar with basic git setup/usage, as well as SSH access to your server.

Setting up the Server

  1. SSH to server
  2. Change directory to project root
    cd public_html/project
  3. Setup repository location and move to that directory
    mkdir -p project-name.git
    cd project-name.git
  4. Setup up a bare git instance
    git init --bare
  5. Setup a post-receive hook for deployment for when a push is made.
    touch hooks/post-receive //create hook
    chmod a+x hooks/post-receive //make hook exectuable

    It is one thing to push code to a remote, like github, but in this case when you push commits to this remote you will then want to checkout the files in order for them to actually deploy and we will be doing that in this hook.

  6. In your favorite editor, edit the newly created ‘post-receive’ script (this would either be with an ftp client, using ‘view/edit’, or from SSH terminal you can use VIM editor if you’re comfortable).Insert this script (modify as necessary for your setup)
    #!/bin/bash
    # 1. Read STDIN (Format: "from_commit to_commit branch_name")
    while read oldrev newrev refname
    do
     branch=$(git rev-parse --symbolic --abbrev-ref $refname)
     if [ "production" == "$branch" ]; then
     `GIT_WORK_TREE=../production git checkout -f production`
     echo "Changes deployed to live site'"
     fi
    if [ "stage" == "$branch" ]; then
    `GIT_WORK_TREE=../stage git checkout -f stage`
    echo "Changes deployed to stage site'"
    fi
    
    if [ "development" == "$branch" ]; then
    `GIT_WORK_TREE=../dev git checkout -f development`
    echo "Changes deployed to development site'"
    fi
    done
    

    This script is triggered whenever a push is made to this remote server. Many things can happen here, but in this example it simply receives the changes from the branches that are pushed (could be a single branch, could be –all). It then determines which branch was pushed and checkouts the changes to defined directory (GIT_WORK_TREE). The branches and directories must match your structure so make sure to change as necessary. Most development teams will at most have a development environment for testing code, a staging environment for stable releases for client review, and obviously a production environment for the live site.

    ***OR if you prefer no branch management (setup a git bare repository for each directory [production, staging, development] and instead use this script

    #!/bin/bash
    
    # 1. Read STDIN (Format: "from_commit to_commit branch_name")
    while read oldrev newrev refname
    do
        `GIT_WORK_TREE=../ git checkout -f master`
        echo "Changes deployed to {environment}"
    done

     

So you see the beauty of this is when a team of developers commits code they can easily `git push <remote> <branch>` and their changes will automatically be deployed to the respective environment.

Adding the Server as a Remote

Now the server is ready to receive a push from git, but just like adding a remote for github you will need to add a remote for your hosting server. This is the same process although you may have to check with your hosting provider about SSH access details. A fairly common example would look like this

git remote add <remote-name> ssh://[email protected]:<port>/home/username/site/project-name.git

You can verify the remotes that you have setup like this:

git remote -v

And if you need to change your path for your server, simply do so like this:

git remote add <remote-name> ssh://[email protected]:<port>/path/dir/project-name.git

Push to Remote

Now the fun part, make a commit and push it to development, staging, production or all three at once.

git push <my-remote> development
git push <my-remote> staging
git push <my-remote> production
git push <my-remote> --all

You can even push local branches to specific remote branches if necesary, like this:

git push <my-remote> development:staging

This will push my local development branch to the staging branch. “It’s so simple!” [Italian accent] I’m glad to help ease the pain of this transition into developer man-hood, I know I couldn’t have done it without a community of mentors in my life. If you have questions or even suggestion to make this better please leave a comment below and I’ll do what I can.

Helpful bonus tip for testing

When testing sometimes you don’t want to keep making commits just to test a new deployment, so don’t. Instead reset your server repository to the previous commit so that when you push your current local changes it appears as a new commit.

git update-ref HEAD HEAD^

Hope that helps someone.