Self hosting git
In this post, I'll walk you through the steps for setting up a simple git
server. We'll be using git daemon
to serve our repositories, and stagit
for the frontend.
Requirements
- C99 compiler.
- Any
patch
implementation. - POSIX complaint shell.
- ~15 minutes of your time.
Setup
Assuming that you already have git
installed on your system, the next step is to build our custom patched version of stagit
.
For building stagit
, we'll require libgit2
, chroma
and cmark-gfm
. The latter two are required by our patch for syntax highlighting and markdown rendering respectively, so make sure that you have all these dependencies installed. Now, we're ready to build stagit
:
-
Obtain the latest
stagit
release tarball from codemadness.org. The patch can be found in my personal KISS repository. The features were originally implemented in this fork ofstagit
, which was further forked here to usechroma
andcmark-gfm
instead of slowpygments
. All credits go to the authors of these two forks, I've just extracted their changes into a single patch. -
Apply the patch and build
stagit
:
patch -p1 < syntax-highlighting.patch
make && make install
Next, we're going to create a few helper scripts to save us some time. The scripts are taken from this article with some modifications made to them.
Create a basic config file at /etc/stagit/stagit.conf
:
GIT_HOME="/var/lib/git/repos"
WWW_HOME="/var/lib/git/home"
CLONE_URI="git://git.mydomain.tld"
DEFAULT_OWNER="username"
DEFAULT_DESCRIPTION="default description"
GIT_USER="git"
post-receive
: git
hook for updating the repository's frontend (Placed in /etc/stagit
).
#!/bin/sh
# Fail if an unset variable is referenced (bad config).
set -eu
. /etc/stagit/stagit.conf
# The hook is called from the repository's root.
src="$(pwd)"
name=$(basename "$src")
dst="$WWW_HOME/$(basename "$name" '.git')"
mkdir -p "$dst"
cd "$dst"
echo "[stagit] building $dst"
stagit "$src"
echo "[stagit] linking $dst"
ln -sf log.html index.html
for file in style.css logo.png; do
ln -sf "../$file" "$file"
done
stagit-gen-index
Place the following scripts somewhere in PATH
:
stagit-gen-index
: Updating the repository index when creating a new repo.
#!/bin/sh
# Fail if an unset variable is referenced (bad config).
set -eu
. /etc/stagit/stagit.conf
stagit-index "$GIT_HOME"/*.git > "$WWW_HOME/index.html"
stagit-new-repo
: Creating a new repository with some boilerplate.
#!/bin/sh
# Fail if an unset variable is referenced (bad config).
set -eu
. /etc/stagit/stagit.conf
if [ $# -gt 1 ]; then
DESC="$2"
else
DESC="$DEFAULT_DESCRIPTION"
fi
if [ $# -eq 0 ]; then
printf "not enough args\n" >&2
exit 1
else
REPO="$(basename "$1")"
fi
git init --bare "$GIT_HOME/$REPO.git"
# Share a common `git` hook between repositories.
ln -sf "/etc/stagit/post-receive" "$GIT_HOME/$REPO.git/hooks/post-receive"
echo "$CLONE_URI/$REPO.git" > "$GIT_HOME/$REPO.git/url"
echo "$DEFAULT_OWNER" > "$GIT_HOME/$REPO.git/owner"
# Ensure that `git daemon` allows repository cloning.
:> "$GIT_HOME/$REPO.git/git-daemon-export-ok"
echo "$DESC" > "$GIT_HOME/$REPO.git/description"
mkdir "$WWW_HOME/$REPO"
Now set up a separate user to serve our repositories:
adduser git
for dir in repos home; do mkdir -p "/var/lib/git/$dir"; done
chown -R git:git /var/lib/git
su - git
mkdir -p ~/.ssh
echo "My public SSH key" >> ~/.ssh/authorized_keys # For pushing to repos.
Almost there, create a git daemon
service for your init system. The following steps are to be followed for runit
based systems (Specifics might vary depending on your distribution):
mkdir -p /etc/sv/git-daemon
cat > /etc/sv/git-daemon/run << EOF
#!/bin/sh
. /etc/stagit/stagit.conf
# Run `git daemon` as our `git` user (security).
exec chpst -u git git daemon --base-path="\$GIT_HOME"
EOF
ln -s /run/runit/supervise.git-daemon /etc/sv/git-daemon/supervise
ln -s /etc/sv/git-daemon /var/service/ # Enable the service
Finally, serve the generated pages through your web server. This should be enough for caddy users:
root * /var/lib/git/home
file_server
Usage
Now that we have our scripts and daemons set up, we're gonna create our first repository and add some CSS!
su - git
stagit-new-repo hello-world
# On the dev system:
SSH_PORT=1234
git clone "ssh://[email protected]:$SSH_PORT/var/lib/git/repos/hello-world.git"
cd hello-world; echo "# Hello" > README.md
git add README.md; git commit -m "Hello World!"
git push
CSS can be generated with the help of chroma
itself, though you'll need to add regular CSS yourself. Here's an example for generating an emacs-themed CSS:
. /etc/stagit/stagit.conf
chroma --html-styles --style=emacs > "$WWW_HOME/style.css"
When creating a new repository, stagit-gen-index
should be run on the server AFTER pushing the first commit. Similarly, it must be run when a repository is deleted.
TIP: The value of GIT_HOME
in /etc/stagit/stagit.conf
can be changed to /home/git
to shorten the cloning URL to ssh://[email protected]:$SSH_PORT/~git/hello-world.git
.
There you have it, a simple git
server with a beautiful JS-free frontend! See mine in action here.