Easily host and manage private git repositories

It is amazingly trivial to setup private git hosting with complete access control via public keys on Linux. Moreover, you can then start to customize for whatever you see fit or need.

If you are a bit familiar with Linux and have basic shell knowledge, it will take you just a couple of minutes to setup a fully working private git repository server.

Rolling your own git hosting comes in handy if you’re working on internal projects, want to mirror or backup a publicly hosted repository, or when you simply have concerns about leaving your code on servers outside your own country’s jurisdiction. Besides, you will have full access to git’s powerful ‘hooks’ system and can customize them as you please, including but not limited to, custom continuous integration, build and deployment work flows.

Prerequisites:

The shortlist of requirements reads as follows:

Walk through

As trivial as it may sound, but all it takes is to use git-shell and provide remote access to respective machine and user.

Code examples assume a somewhat recent Linux distribution and should work out of the box on most distributions. If in doubt, check the man pages before executing any of the code examples.

Prepare the host machine

First we’re going to create a new unprivileged account on the remote machine and call it git. Of course, you can use whatever username you want, the only important part is specifying git-shell.

sudo useradd -s /bin/git-shell git

Our newly created git account is pretty useless right now, unless we add some commands, which will be the building blocks for any future actions:

GITHOME="/home/git" # change this if you customized above command
sudo mkdir $GITHOME/git-shell-commands
sudo cat > $GITHOME/git-shell-commands/help <<\EOF
#!/bin/sh

if tty -s
then
        echo "Run 'help' for help, or 'exit' to leave.  Available commands:"
else
        echo "Run 'help' for help.  Available commands:"
fi

cd "$(dirname "$0")"

for cmd in *
do
        case "$cmd" in
        help) ;;
        *) [ -f "$cmd" ] && [ -x "$cmd" ] && echo "$cmd" ;;
        esac
done
EOF

sudo cat > $GITHOME/git-shell-commands/init <<\EOF
#!/bin/sh

echo "Initialize new bare git repository:"

if [ ! "$1" ]; then

  echo "Usage: $0 <directory>"
  echo ""

elif [ -d "$1.git" ]; then

  echo "INFO: directory already exists"

else

  echo "Initialization new repo $HOME/$1"

  mkdir -p "$HOME/$1.git"

  cd "$HOME/$1.git"

  git --bare init

  echo "DONE."

fi
EOF

sudo cat > $GITHOME/git-shell-commands/list <<\EOF
#!/bin/sh

print_if_bare_repo='
        if "$(git --git-dir="$1" rev-parse --is-bare-repository)" = true
        then
                printf "%s\n" "${1#./}"
        fi
'

find -type d -name "*.git" -exec sh -c "$print_if_bare_repo" -- \{} \; -prune 2>/dev/null
EOF

sudo chmod +x $GITHOME/git-shell-commands/*

Now log in as git and you will be prompted by something like this (try typing help):

Last login: Fri Feb 21 13:33:02 2014 from [redacted]
Run 'help' for help, or 'exit' to leave.  Available commands:
init
list
git>

As you can see, git-shell will accept any script (or program) located at $HOME/git-shell-commands. It does not have to be a shell script at all! As long as its readable and executable, it will become available as a command to be executed. If there is a executable called help it will be recognized as such! I will leave it to your imagination what is possible now …

Apropos readable: Lets change ownership and permissions of all git-shell commands (and the folder git-shell-commands itself), so only a user with administrative rights to the machine is capable of adding, changing, or deleting the scripts. Choose whatever user/group you feel comfortable with. For the sake of simplicity, I use root.

sudo chown root:root /home/git/git-shell-commands -R
sudo chmod u+rwx,g-w,o-r /home/git-shell-commands -R

Enable remote access

Depending on your Linux distribution of choice, you either had to set a password for the git user already, or it has been initialized without any password. In the latter case, set a password like so on the remote host:

$ sudo passwd git

Assuming you and/or your colleagues have public ssh-keys provided, copy them over the remote host, so you can access the git repositories without having to type passwords again. Be sure to change the hostname before copy-n-pasting below snippet!

ssh-copy-id -i /path/to/public.key [email protected]

After entering the git user’s password (one last time), the credentials will be copied over to ~/.ssh/authorized_keys and you can use respective public keys to connect to the git host, execute all commands available in ~/git-shell-commands; as well as do your usual git work flow remotely.

Its a good idea to revoke the old git user’s password on the remote host now, and also to no longer permit password authentication via ssh at all.

Your first self-hosted git repository

Lets say, you already have a local repository available you want to share on your newly available remote machine:

# create bare repository on the remote
$ ssh [email protected]
$ init my/shiny

# on your local machine
$ git remote add myshiny [email protected]:my/shiny.git
$ git push myshiny

Customization

As outlined before, you can create your own scripts, put them into ~/git-shell-commands and be merry ever after. Also, every repository you create will have a hooks folder of which you have full control over. By default there are example files in there you could simply strip of the .sample suffix. For more information consult your friend the manual page via man githooks :)

Bonus points

Over time and with many developers it becomes cumbersome to manually manage the ~./ssh/authorized_keys file. Additionally, it probably will be more and more important to have fine-grained access control for each of the hosted repositories.

In this case, its worthwhile to look into gitosis. Gitosis will make it a lot easier to manage access to your hosted repositories. In order to use gitosis, you will have to have python’s setup-tools installed on the remote machine. If you’ve come this far, it should not be any problem to just follow the steps outlined at http://git-scm.com/book/en/Git-on-the-Server-Gitosis.