Commented Code ...blog

Oct 29, 2012

Creating a Public Access, Read-Only Repo...using only Git

So you have a Git repository running on a server and you want to have public access via http but you don't feel like installing some fancy Git web service. We can use some built-in features of Git to generate our web-access output, toss in a little nginx or lighttp and we have a super simple setup.


The setup

First step is to create a location where we will store our read-only repo. Depending on your servers setup, you'll need to make sure that the user connecting to Git has access to write here. On my server all my users have a $HOME/html that converts to http://locahost/<user>.

mkdir ~/html/devel

The hook

When you initialize a repository directory on your server a couple of files and directories are created. Most are the paperwork to keep the whole version control thing working but the ones we want are contained in the hooks directory. Reading through the documentation on hooks we find that there is a plethora of cases when a transaction between a Git client and server including before and after applying a patch, a commit, a checkout or a merge.

So the next step is to create your repository, initialize, check-in, etc. Once you have your repo working we can go in and edit our hook. Remember, this is on the server side and not in your local instance of the repo.

~/git/proj.git> ls
HEAD         description  info         refs
config       hooks        objects

We want to create a hook that updates our web access whenever changes are applied to the server. For this we will edit post-update which will be triggered any time a client performs a git push to the server. Makes sense. The job of this script is to copy the git server's files to our web server, do a little clean up and exit out gracefully. So here we go...

#!/bin/sh
#
# File: ~/git/proj.git/hooks/post-update
#
# Description: Called when an update is pushed to the server
#

# Location of your repo on the server
GIT_DIR=/home/jecxjo/git/proj.git

# Location of your public version of the repo
HTTP_DIR=/home/jecxjo/html/devel/proj.git

# Update local repo info
git update-server-info

# Make sure a clean copy is moved
rm -rf $HTTP_DIR
cp -rf $GIT_DIR $HTTP_DIR
chgrp -R nobody $HTTP_DIR

# Directories must have Read and Execute Permissions
# for apache to be able to navigate them.
for d in `find $HTTP_DIR -type d`; do
    chmod a+rx $d
    done

# Files must have Read Permissions for apache
# to be able to read them.
for f in `find $HTTP_DIR -type f`; do
    chmod a+r $f
    done

# Display a message on the client side to show 
# the action has been performed.
echo "Updated Public Access"

For the most part the comments are self explanatory. We start by making sure our server's copy of the repo is all clean and tidy using git update-server-info. Next we clear out our web directory, copy, change permissions, etc.

NOTE You may have to do a little bit of permissions work here depending on your web server.

The last line is actually kind of interesting. Whatever is printed by the script is displayed by the client after a git push. For the moment we will just print that our public access has been updated, but all sorts of information could be printed here.

Last step is to make sure the script can run.

chmod a+x ~/git/proj.git/hooks/post-update

You can run the script directly from your terminal or perform a push to get your site updated.

$ git push
Counting objects: 5, done.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 256 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Updated Public Access
To jecxjo@server:git/proj.git
   e60a9de..1f8a43f  master -> master
$

We see the line remote: Updated Public Access which tells us that our hook executed. Thats how we get public and private access. Very easy, no 3rd-party services, just git...plain and simple.

Private: git clone jecxjo@server:git/proj.git

Public: git clone http://jecxjo.server/devel/proj.git

<perm> | /devel | 2012.10.29-17:16.00 | <comments> | <reddit> | <digg> | <stumbleupon> | <tweet>