Navigation

Making a Public Git Server

In the sense of having a Git server, with a web front-end, which can be cloned from by unauthenticated randoms (but not pushed to). Obviously, you need to have Git installed, and you need SSH access to your server. I'll let you work that much out.

In setting up a basic Git server for just your own use, refer here. There isn't much use in reiterating what has already been said.

You now need 3 things - your public SSH key; a repository on your machine; and a 'bare' repository directory on the remote server. Run ssh-keygen if you don't already have a key. You may safely leave the password blank, I prefer to go without so I won't be prompted.

You may or may not like to add the directory 'git-shell-commands' to the home directory of your Git user, this allows a Git prompt and the ability to log in as the Git user.

If you haven't done so already (this is a system-wide procedure), you may also want to disable password SSH logins, and rely purely on your key. See '/etc/ssh/ssh_config' and '/etc/ssh/sshd_config', or your system's equivalent.

Having Completed the Above

You should be able to push and pull to your repository with your SSH key. You may add collaborators to '~/.ssh/authorized_keys' also. However, this isn't particularly useful yet - in order to clone from your repository, users would need to have an authorized SSH key. SSH by design only allows for authenticated logins. So, you need to depend on HTTP.

A Git Server with HTTP Read (but not Write) Access

First of all, just make sure you have an open HTTP (80) or HTTPS (443) port.

You'll need to set up the Git daemon, to allow exporting of given repositories. It's simple enough to just run the command, but you probably would want to put it into an init system service file. Here's an example for systemd:

[Unit]
Description=Start Git Daemon

[Service]
ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/path/to/git/root/ /path/to/git/root/
Restart=always
RestartSec=500ms

StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=git-daemon

User=git
Group=git

[Install]
WantedBy=multi-user.target

With the daemon enabled, add an empty file called 'git-daemon-export-ok' in the base directory of each server-side repository. In each repository, you will find a directory called 'hooks'. The hook 'post-update' must be enabled - this is as simple as moving 'hooks/post-update.sample' to 'hooks/post-update'. For this to take effect, run 'git update-server-info'.

You should now have a working HTTP Git server. No one has write access, nor can they SSH into your server without the correct authentication.

But I Need a Front-End

Yeah yeah, I'm getting to it. You could in theory use whatever front-end you like: I'm going to talk about the one I use, stagit. If you're interested, keep reading, and clone and compile it on your server.

Repository pages are created by entering the repository directory, and running 'stagit ./'. A listing of repositories can be created by running 'stagit-index dir1/ dir2/ dir3/ > index.html' for each repository. To get your web front-end to reflect the most recent changes, you'll need to run stagit again.

Stagit formats the pages with 4 files:

You can set these to be different values for every repository directory - but you likely want them to be consistent. Rather than maintain many copies of them, you can simply link them to each repository with 'ln -s ../style.css', etc.

This would be a massive pain to manage for every update, which is exactly why I wrote a script. You may find yourself adjusting it slightly for your own needs.

wd=$(pwd)
cd /path/to/git/root/
sudo -u git bash -c 'stagit-index $(find -maxdepth 1 -type d | grep /) > index.html'
sudo -u git bash -c 'for dir in $(find -maxdepth 1 -type d | grep /); do
	cd "${dir}"
	find style.css >/dev/null 2>&1; [[ $(echo $?) == 1 ]] && ln -s ../style.css
	find favicon.png >/dev/null 2>&1; [[ $(echo $?) == 1 ]] && ln -s ../favicon.png
	find logo.png >/dev/null 2>&1; [[ $(echo $?) == 1 ]] && ln -s ../logo.png
	find url >/dev/null 2>&1; [[ $(echo $?) == 1 ]] && formatdir=$(echo ${dir} | cut -d / -f 2) && echo "git://git.example.com/${formatdir}" > url
	find git-daemon-export-ok >/dev/null 2>&1; [[ $(echo $?) == 1 ]] && touch git-daemon-export-ok
	find hooks/post-update >/dev/null 2>&1; [[ $(echo $?) == 1 ]] && mv hooks/post-update.sample hooks/post-update && git update-server-info
	stagit ./
	cd ..
done'
cd $wd

You can run the script manually, but a better option would be to make it a cron job. An even better option still would be to use the 'post-receive' hook to run the script upon detecting a push, which I do on my server.

Following this, you should have a web server serving Git over HTTP/HTTPS which people can clone using a web interface, but which only you and your collaborators push changes to. Enjoy.