Git Server
Down to the minimum, a git "server" could just be a bare repo located somewhere in the filesystem:
$ git init --bare repo.git
$ ls repo.git/
$ git clone repo.git
$ ls repo/
Thus it's possible to remotely access it using NFS or SSH.
Since the repo is just a directory on the filesystem, it uses the filesystem permissions. It's not supported for multiple users to be able to write to the bare repository, and only the owner can do so. Also, there're no permission control or branch protection because the owner could easily override things in that directory.
Thus, advanced permission manage solutions exist, like Gitea, GitLab, GitHub, or if you prefer something simpler, gitolite.
Gitea, GitLab, and GitHub are complete Git solutions that not only does bare Git repo management, but also integrates permission control, web browsing, CI / CD, and lots more. They are also easier (somehow) to setup, and may be suitable and easy for more people (especially for those prefer pull requests over email patches).
Git -> HTTP Interface
It's awesome to have a HTTP interface that allows users to preview the repo without cloning it alltogether. Besides complicated all-in-one solutions like GitLab, simple programs exist, notably gitweb and cgit.
It is worth noting that the web interface has nothing todo with HTTP cloning. HTTP cloning is a complete different protocol using HTTP, and it is handled using a different program.
gitweb is the default web interface shipped with git. I used it in the past but quickly replaced it with cgit for just a little more fancy features.
cgit is developed by Jason A. Donenfeld (the person who developed WireGuard), and it is actively maintained. Its git repo is at git.zx2c4.com/cgit.
Both gitweb and cgit are FastCGI programs. Make sure you have a basic understanding of how FastCGI works before proceeding.
And here is my Nginx configuration for cgit:
root /usr/share/webapps/cgit/;
location ~* ^.+\.(css)$ {
root /usr/share/webapps/cgit/;
}
location /robots.txt {
root /usr/share/webapps/cgit/;
}
location ~* ^.+\.(png|ico)$ {
root /srv/http/cgit/;
}
location / {
include fastcgi_params;
index cgit.cgi;
fastcgi_param SCRIPT_FILENAME $document_root/cgit.cgi;
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_param HTTP_HOST $server_name;
fastcgi_pass unix:/run/fcgiwrap.sock;
}
The configuration is pretty straightforward and self-explanatory. Configuring
HTTP rewrite could be a little bit troublesome, though. In my configuration, I
set virtual-root=/
in cgitrc(5)
to solve path issues.
The cgit configuration file cgitrc(5)
is at /etc/cgitrc
, and it is also
pretty straightforward. Some key points in my setup are:
# Solve path issues
virtual-root=/
# Enable source highlighting. Slow
source-filter=/usr/lib/cgit/filters/syntax-highlighting.py
# Enable README rendering.
about-filter=/usr/lib/cgit/filters/about-formatting.sh
readme=:README.md # Enable readme preview. Copied from cgitrc(5)
# Append more readme=: here.
# Automatically load repos from the path.
scan-path=/srv/git/
clone-prefix=https://git.yuuta.moe
cgit itself does not do authentication. My friend wrote a tool called cgit-simple-authentication which may sound interesting to you.
HTTP Cloning
HTTP cloning is handled by git-http-backend
, a FastCGI application shipped
with git. Running it doesn't require any configuration except a set of
well-defined HTTP path regular expressions, so the web server works fine with
both HTTP cloning and cgit in the same domain. Here's a Nginx configuration I
got from the Internet:
location ~ ^.*\.git/objects/([0-9a-f]+/[0-9a-f]+|pack/pack-[0-9a-f]+.(pack|idx))$ {
root /srv/git/;
}
location ~ ^.*\.git/(HEAD|info/refs|objects/info/.*|git-(upload|receive)-pack)$ {
root /srv/git/;
fastcgi_pass unix:/run/fcgiwrap.sock;
fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend;
fastcgi_param PATH_INFO $uri;
fastcgi_param GIT_PROJECT_ROOT $document_root;
fastcgi_param GIT_HTTP_EXPORT_ALL "";
fastcgi_param REMOTE_USER $remote_user;
include fastcgi_params;
}
In this configuration:
- Repos must be suffixed
.git
. Without.git
it will be directed to cgit. - The path of git repos are
/srv/git
, the same asscan-path
incgitrc(5)
.
Hooks
Script to run as the current user during certain actions (e.g., after pushing).
They are located in bare repo hooks/*
.
Post receive
The hook to be executed after each hook. I'm using a simple (and ugly) script to build my website. It is absolutely better to use some kinda solutions like Jenkins.
#!/bin/sh
set -e
GITDIR="$(pwd)"
BUILDDIR="/tmp/build_kb_$(date +%s)"
BRANCH=$(cat | sed -e "s/[a-z0-9]* [a-z0-9]* refs\/heads\/\(.*\)/\1/g")
if test "$BRANCH" != "master"; then
exit 0
fi
echo "Deploying ..."
git clone --recurse-submodules -q $GITDIR $BUILDDIR
cd $BUILDDIR
set +e
unset GIT_DIR
make
set -e
cd $GITDIR
rsync -r --delete $BUILDDIR/site/ /srv/http/kb/
rm -rf $BUILDDIR
Environment variables
A number of environment variables will be added when executing the script, and this may affect git(1) operations inside the script. Take them with cautious. The following example may not refelect the actual value in your setup.
$ env | grep GIT
GIT_DIR=.
GIT_EXEC_PATH=/usr/lib/git-core
GIT_PUSH_OPTION_COUNT=0
Created: November 5, 2023