How to make your own git server/website

Server setup

We’re going to create a git user and group, will be used for accessing our git repositories without needing to be root.

useradd -m git
mkdir /srv/git
chown git:git /srv/git

The repositories will be stored as bare, this mean that we will only store the .git folder to save space.

To create a bare repo: git init --bare

To clone a repo as bare: git clone bare <location>

You can either add a password for the git user or put your ssh public key in /home/git/.ssh/authentication_keys and disable password authentication for ssh.

You can now clone from your server with git clone git@<hostname>:/srv/git/<reponame>

Better server interaction with git-shell

Permit the git user to have a regular shell can be a security issue, we would like that to restrict him to a few action, like creating/deleting a repository, importing a repository, listing the repo currently stored

Make the git-shell a valid shell echo $(which git-shell) >> /etc/shells

Change the shell of the git user chsh -s $(which git-shell) git

If you try to ssh as the git user, you will be greeted with something along the line of:

fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to <host> closed.

As suggested by the hint we have to create the directory /home/git/git-shell-commands and put the commands (executable) that the git user is allowed to execute in.

Here is a script to create a repo:

#!/bin/sh
[ $# -ne 1 ] && echo "Usage: $0 name" && exit 1
repo_path="/srv/git/$1.git"
[ -d "$repo_path" ] && echo "$0: Error: $repo_path already exist" && exit 2
mkdir "$repo_path"
git -C "$repo_path" init --bare

Put it under git-shell-commands/create and make it executable then try to ssh as the git user once again.

You should have a prompt like git>, you can call the create command with a repo name as the first argument and it should create a new repository for you.

Allow anyone to clone with git-daemon

The git daemon will allow annone to clone your repos with something like git clone git://<host>/<repo>

git daemon --reuseaddr --base-path=/srv/git/ /srv/git/ and that’s it

You should make it a service in your service supervisor, example with systemctl:

[Unit]
Description=Start Git Daemon
[Service]
ExecStart=/usr/bin/git daemon --reuseaddr --base-path=/srv/git/ /srv/git/
Restart=always
RestartSec=500ms
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=git-daemon
User=git
Group=git
[Install]
WantedBy=multi-user.target

Put it in /etc/systemctl/system/git-daemon.service and run systemctl enable git-daemon then systemctl start git-daemon.

Public/private repo

You may want to introduce a distiction of which repo is public and which is private

A simple way to do this is by creating a public directory in /srv/git which will contain symbolic link to the repo in /srv/git

/srv/git
|- foo.git
|- bar.git
|- qux.git
|- public
    |- foo.git -> /srv/git/foo.git
    |- bar.git -> /srv/git/bar.git

You can change the git daemon to only serve the public repositories git daemon --reuseaddr --base-path=/srv/git/public /srv/git/public

Generate a static website

Here we will create a site that look’s like this with nginx, stagit and a few scripts.

If you don’t like the minimalistic appearence of the site, here is a list of alternative.

Install nginx (on Debian based distro): apt install nginx

Create a basic configuration file for your site:

server {
    root /var/www/git;
    index index.html index.htm;

    server_name git.<hostname> www.git.<hostname>;

    location / {
        try_files $uri $uri/ =404;
    }
}

It’s a convention to put it in a git. subdomain. systemctl enable nginx && systemctl start nginx

Install stagit:

git clone git://git.codemadness.org/stagit
cd stagit
make
make install

To generate a static page for a repo stagit /path/to/repo. To generate an index for multiple repositories stagit-index repo1 repo2 repo3 > index.html

Here is a script to generate a site for all repo in /srv/git/public

#!/bin/sh

repos=$(find /srv/git/public/ -type l)
current=$(pwd)

for repo in $repos; do
    repo_name=$(basename "$repo" | sed 's/\.git//')
    repo_static_path="/var/www/git/$repo_name"
    mkdir -p "$repo_static_path"
    cd "$repo_static_path" || exit 1
    echo "git://cacharle.xyz/$repo_name" > "$repo/url"
    stagit "$repo"
    ln -sf "$repo_static_path/log.html" "$repo_static_path/index.html"
    echo "Generated $repo_static_path"
done

echo "Creating index"
stagit-index $repos > /var/www/git/index.html

cd "$current" || exit 1
chown -R git:git /srv/git

There is more smart ways to handle this to rebuild the webpages each time someone pushes to the repo with git hooks.

Sources