Wednesday, August 31, 2011

Configuring a GIT controlled Web site

This is assuming you're using a *nix setup for the server, and Mac OSX for the workstation.

First, register your public key with the server's SSH. On a shared host, this would typically be under a control panel icon for Shell Access or SFTP/SSH . Consult your hosting service for details.

Suppose your site is "" and your user name is "jonathan".
You should be able to log in to your site. Open a command terminal window, and:


If you can't do that and get a shell prompt, you're hosed. There are ways around it, but that's another post. Knowing how to navigate this level of complexity is a sort of prerequisite... you don't need to know, just know how to find out how to do this kind of stuff.

So, you are logged in to your server, and you create a subdirectory for your repositories, and a directory for your web site or app:

mkdir -p ~/repositories/

You initialize that directory as a "bare" git repo. (A bare git repo has no modifiable source files present, just the repository metadata and data files). This will be the repo you will stage changes to immediately prior to deploying:

cd ~/repositories/
git init --bare

Now, you switch to another terminal, either another window or a tab. Create and/or change your to the working directory for your lili.pops site:

mkdir -p ~/workspace/
cd ~/workspace/

Now, at this point, we're assuming that there are oodles and oodles of cool source files with names like "index.html" and "flavor_picker.js" just sitting around in ~/workspace/

We can (we should, nay, we must!) turn the directory into a git repo (a non-bare repo with source files, that is):

git init
git add .
git commit -m "initial commit of content"

This is the really weird part:Git links up repositories very loosely. Git keeps data about the sets of files that are changed, which it calls a "commit". These commits can be pushed by you to bare repositories, or pulled by people working with you into their own repositories.

So people contribute independently. Note also, that it isn't necessary for everyone to have complete and utter exposure to all of your files.

Let's link our repository to the bare repository on the server:

git remote add staging ssh://

# (Uhhhh.... long line folding is making this look wrong. It should all be one line.)

What we are doing here is simply making an association between a local label "staging", and a url that points to the server's repository. In this case, we're using the SSH protocol to make the link. Since we provided a public key, we aren't going to be asked for a password in the next step.

The next step is to set up a branch on the remote that will track our local main line branch, and copy our changes to it:

git push staging +master:refs/heads/master

The "+master:" part means "create a master branch on the remote repository".
The "refs/heads/master" part means "compare the remote branch's references to references from the local head master branch".

Once that association has been made between the local and remote repositories, the command to push our changes is much simpler: we just refer to the label we used ("staging"):

git push staging

Go ahead, repeat that command. You should see:

Everything up-to-date

At this point, all we've done is pushed changes to a remote repository. No one can see our files because that remote is "bare" -- there is no working source tree. The most obvious way to deal with this is to go to the server and ask git to pull out a copy of the working files into some web root location.

Switch back to your SSH command window, and checkout the changes on the server:

GIT_DIR=~/repositories/ GIT_WORK_TREE=~/public_html/staging/latest git checkout -f

Here, I'm assuming that you use public_html/staging/latest as a location to push your changes to before they've been finally released.

Now, that's all that is necessary, but we can streamline the process by automating that last step. There are two ways to do this. One is by way of a shell script, which I'll call "deploy":
user = ${1%%@*}
server = ${1##*@}
branch = $2
git push $server $branch
ssh ${user}@$server "GIT_DIR=~/repositories/ GIT_WORK_TREE=~/public_html/staging/latest git checkout -f"

Well, that's a rough guesscript. It looks like it would work, but I haven't tested it.

Another way is by a Git "hook". Go to the SSH window, and run the following:

cd ~/repositories/
cat > "hooks/post-update" <<EOT
GIT_DIR=~/repositories/ GIT_WORK_TREE=~/public_html/staging/latest git checkout -f
chmod u+x "hooks/post-update"

Now, whenever you do a git push staging, the post-update hook on the server will do the checkout for you.

Note: if you are deploying applications, you probably already use a .htaccess file or alternatives to hide or block access to resources that aught not to be exposed on your site.

1 comment:

Unknown said...

Note: the GIT_DIR and GIT_WORK_TREE in the last script should be on the same line.