A simple, powerful self-hosted git setup

I had been using gogs for about a year. It worked reasonably well, as it focuses on being a lightweight self-hosted GitHub replacement. However, that wasn’t really what I wanted. I just wanted to host my own projects, I didn’t need things like issues, pull requests or wikis.

I recently switched to gitolite and cgit, as they were even lighter on resources, don’t require another login and work without an external database. Gitolite is unusual in its configuration: it creates a git repository with its configuration file. I will describe how I use them, rather than how to set them up, as they both have enough documentation on that.

My gitolite configuration file looks like this:

repo gitolite-admin
    RW+     =   alan

repo dotfiles
    C   =   alan
    RW+ =   alan
    R   =   READERS
    option hook.post-update   =    github-mirror

repo [a-z].*
    C   =   alan
    RW+ =   CREATOR
    RW  =   WRITERS
    R   =   READERS

The first block just allows me to work with the configuration repository, as the initial setup only enables one specific public SSH key, whereas I have three keys that I configure gitolite with.

The second configures my dotfiles specifically. Naturally, I should be the only person with read/write access. The R = READERS line allows remote configuration of read permissions via ssh $DOMAIN perms (explained further below). The last line runs a mirror script (just git push --mirror…) so that my dotfiles repository on GitHub is updated when I push to my private version.

Wild (or magic) repositories

The third block is where things get interesting. gitolite has a feature called wildrepos, which allows configuring a set of repositories at once, using a regular expression to match the repository name.

The really nice thing here is that the repository need not exist before applying the configuration. Therefore, the line C = alan means that I can create a remote repository automatically by cloning a repository URL that doesn’t already exist. I can clone and create a new repo simultaneously like so:

cd ~/projects
git clone alanpearce.eu:some-new-repository

But with ghq, which I blogged about before, I don’t have to concern myself with where to put the repository:

$ ghq get alanpearce.eu:some-new-repository
     clone ssh://alanpearce.eu/some-new-repository -> /Volumes/Code/projects/alanpearce.eu/some-new-repository
       git clone ssh://alanpearce.eu/some-new-repository /Volumes/Code/projects/alanpearce.eu/some-new-repository
Cloning into '/Volumes/Code/projects/alanpearce.eu/some-new-repository'...
Initialized empty Git repository in /var/lib/gitolite/repositories/some-new-repository.git/
warning: You appear to have cloned an empty repository.

The nice URLs come from this piece of my SSH configuration:

Host alanpearce.eu
  HostName git.alanpearce.eu
  User gitolite

Configuring wild repositories

This repository would be private by default, but I can change that by an SSH command. Here’s how I would do it:

ssh alanpearce.eu perms some-new-repository + READERS gitweb
ssh alanpearce.eu perms some-new-repository + READERS daemon

The first command makes it visible in cgit, whilst the second makes it clonable via git:// url. I can make a repository publically-clonable, but invisible on cgit by only allowing the daemon user and not gitweb, if I wanted.

I can also add or change the description of a repository shown on cgit like so:

ssh alanpearce.eu desc some-new-repository 'A new repository'

All the remote commands exposed by gitolite are described in the help command e.g. ssh alanpearce.eu help

hello alan, this is gitolite@oak running gitolite3 (unknown) on git 2.12.2

list of remote commands available:

	D
	desc
	help
	info
	motd
	perms
	writable

Conclusion

I much prefer creating repositories in this way. It’s much simpler and allows me to get on with working on the repositories rather than going through a multi-step process in a web browser.

With cgit and gitolite, I have a minimal setup, that does exactly what I want, without consuming many system resources with daemons.