If you are new to the world of distributed version control systems such as Git, it can be hard at first to wrap your head around how the hell it actually works. Or never mind that, just getting it to work can be a struggle in itself!

I remember when I got started, in my case with Mercurial and Bitbucket (similar to Git and GitHub). I was tasked with figuring this stuff out for my team and I really struggled wrapping my head around the various concepts, figuring out how it all fit together, how Git Bitbucket factored into all this and finally working out the most basic workflow from all the scattered, conflicted and (for me) far too advanced advice I found on the internet. When I finally managed to figure out branches, commit some changes, have them pushed and checked out somewhere? You know, I might have done a little dance in the office! And trust me, that never happens. I was really, really happy and proud of myself. And relieved. Immensely relieved.

xkcd's take on getting started with Git...

Maybe you are a solo developer. You've heard about Git and how awesome it is and you have reached the point where you are able to do the basic actions and get your code on GitHub. Cool. But how do you actually go from there to having your code deployed on the production server?

You've probably cloned a couple of Git repositories from GitHub, so your first attempt was likely to simply clone your own Git repository into the destination directory of your server and pull whenever you made changes. This works. But you really, really shouldn't do that. You could mess around with Apache's .htaccess, denying access to the hidden .git directory, but it is easy to forget and I don't think Nginx even has an equivalent. There's a much better solution using a bare repository and a post-receive hook. Sounds complicated, but it really isn't. Let me show you.

Create a new repository

First, create a new directory coolapp somewhere on your computer. Let's create a new Git repository inside it with git init.

Next, create a new file in this directory, for example a readme.md file with hello world written in it. Let's add the file to our Git repository and commit it:

git add .
git commit -m "my first commit"

Visit GitHub, login to your account and create a new private repository. I'll assume your username is solodev and your repository is aptly named coolapp, so https://github.com/solodev/coolapp.

We want to link the local repository on your computer with this new remote repository on GitHub, so let's do that. On your computer:

git remote add origin git@github.com:solodev/coolapp.git
git branch -M master
git push -u origin master

Great, the repositories are now linked. Feel free to make some changes, commit those changes and do a git push. So far so good. The next step is to get your files deployed onto the production server.

Prepare the server

Before we proceed any further, you'll need to make sure your production server is set up correctly. If you are currently using a password to login your server, it is not set up correctly. Please follow these instructions to switch to authentication with SSH keys before proceeding.

Ready to continue? Please login to your production server.

The way this is going to work is that we use a bare repository (a repository without checked out files) and a post-receive hook, which is just a script that places the files in the correct deployment directory on the server.

Assuming your coolapp is stored and served from /var/www/coolapp, lets create the directory var/git to store our bare repositories and give this directory appropriate user rights. Then create the subdirectory for coolapp. For example if you are logged in as solodev:

sudo mkdir /var/git
sudo chown solodev:solodev /var/git
mkdir /var/git/coolapp

Create a bare repository & write the post-receive hook script

Inside var/git/coolapp, we are going to initialize a new bare repository:

git init --bare

Next, we are going to write the post-receive hook script with nano hooks/post-receive:

#!/bin/sh

git --work-tree=/var/www/coolapp --git-dir=/var/git/coolapp checkout -f

Save the file and make it executable:

sudo chmod u+x,g+x hooks/post-receive

You are almost there. The final piece of the puzzle is to give the bare repository on the production server actual changes to work with, which it will then checkout into the working directory where your coolapp is served from.

Back on your own computer, go to the coolapp directory. Open the hidden .git/config file with your favorite editor. You will find a section like this:

[remote "origin"]
	url = git@github.com:solodev/coolapp.git
	fetch = +refs/heads/*:refs/remotes/origin/*

We are going to modify it like this (with the pushurl):

[remote "origin"]
	url = git@github.com:solodev/coolapp.git
 	pushurl = ssh://solodev@yourserver/var/git/coolapp
	fetch = +refs/heads/*:refs/remotes/origin/*
(change yourserver to the ip address or hostname of your server)

And that's it! To test it out, make some changes, commit & push. You'll notice that your GitHub repository was updated and your server at the same time as well.

How cool is that?

Further reading

I hope this helped you. I should note that this method breaks down quickly if you work in a team or have a more complicated setup with staging servers and/or multiple production servers.

If you would like to read more, check out:

Photo by Ketut Subiyanto from Pexels