source control under control
the state of git
git is an amazing piece of software for keeping your source code organized and your changes tracked and reversible. Created by Linus Torvalds, the mind behind the Linux kernel, it is the de facto source/version control software used almost everywhere in the industry now. One of the most beautiful things about it is that it is decentralized -- meaning it can be hosted anywhere, and you can copy/change the upstream repository at any time, with no side effects.
If you've heard of Git, you almost certainly heard it in the context of Github. To many people, the two terms are synonymous. Github just happens to be the most popular server used as an upstream source for software repositories. It was created from the ground up in 2008, and 10 years later was purchased by Microsoft. Why is Github so popular? Well, their web interface made many aspects of collaborating on and managing large software projects more convenient, and it made backing up your code more accessible. It blew BitBucket (created also in 2008 and purchased by Altassian in 2010) out of the water with its interface, ease of use, and features, and it still does. Gitlab, created in 2014, is a strong competitor in this space too, and aquired a lot of new business and users after Github was purchased by Microsoft. But Github still dominates.
I had been using Gitlab to host my projects since the Github buyout by Microsoft in 2018, but I realized that I didn't really trust Gitlab much more than Microsoft (granted, 99% of my personal code is public anyways, but it's more of a principles issue; update 2021-09-18: it turns out the flagship instance at gitlab.com has a number of ethical issues regarding privacy, accessibility, and software freedom) and the reliance of the web interface on Javascript is less than desirable. So the search for a more minimal and DIY solution began. Gogs, Gitea, and the like are pretty plug-n-play, but heavier than what I'm after and use Javascript for the frontend. SourceHut seems like a great platform, but the dependencies are a bit high for my server which shares duties as a general webserver. But, hmm, I seem to remember seeing a minimal self-hosted git interface on kernel.org and other places...
cgit to the rescue
Enter cgit -- an old-school web frontend for git repositories using the CGI protocols and written in C, it is dead-easy to setup, requires no Javascript to run, and (assuming you already have a machine to host your code) puts your projects back under your control.
I found this post (on a github.io page ironically) breaking down how to install and setup cgit
on a Raspberry Pi running Alpine Linux, and it was easy enough to adapt it to my own setup on Void Linux.
(2021-10-26: I have now migrated to an Uberspace from my Void Linux VPS, but aside from changing the paths in the lighttpd
configuration to reflect a user-scoped installation of cgit
from source, the configuration is the same!)
On Void Linux, cgit
is easily installable by the package manger:
# xbps-install cgit
And the configuration file at /etc/cgit/cgitrc
is pretty straightforward. Here is my setup:
css=/cgit/nilfm.css logo=/cgit/nilfm_blackHole_96.png logo-link=https://nilfm.cc favicon=/cgit/nilfm_blackHole_32.ico head-include=/usr/share/webapps/cgit/head.html virtual-root=/git enable-git-config=1 enable-index-owner=0 enable-commit-graph=1 enable-index-links=1 enable-log-linecount=1 enable-log-filecount=1 #cache-size=512 robots=noindex, nofollow root-title=nilFM — hack lab root-desc=black hole server remove-suffix=1 clone-prefix=https://hacklab.nilfm.cc mimetype.gif=image/gif mimetype.html=text/html mimetype.jpeg=image/jpeg mimetype.pdf=application/pdf mimetype.png=image/png mimetype.svg=image/svg+xml about-filter=/usr/bin/lowmd readme=:README.md readme=:readme.md readme=:README readme=:readme clone-prefix=https://hacklab.nilfm.cc section=9 scan-path=/home/nilix/src/9/ section=etc scan-path=/home/nilix/src/etc/ section=games scan-path=/home/nilix/src/games/ section=web scan-path=/home/nilix/src/web/
My custom CSS file is available here for reference. In addition to theming the cgit
interface to match the rest of my site, it also includes some media query rules to make the interface mostly usable on mobile (combined with the head-include
entry in the cgitrc
which just sets the "viewport"
meta property to "initial-scale=1"
). The about-filter
setting tells cgit
how to parse README
files for displaying them in the about tab of the repository. It points to a shell script which wraps lowdown like so: cat | lowdown
to discard the filename argument that would otherwise be passed to it and break everything.
lowdown
is vastly superior to the python script md2html
which is included with the cgit
distribution -- it's fast compiled C with no dependencies, makes no assumptions about CSS styling (letting me handle it myself), and properly handles image links in the markdown source.
My server uses lighttpd, so a quick few lines in /etc/lighttpd/lighttpd.conf
expose the CGI interface and cleanup the URLs:
server.modules += ("mod_redirect", "mod_alias", "mod_cgi", "mod_fastcgi", "mod_rewrite", "mod_alias",) var.webapps = "/usr/share/webapps/" $HTTP["url"] =~ "^/cgit" { setenv.add-environment += ( "CGIT_CONFIG" => "/etc/cgit/cgitrc" ) server.document-root = webapps server.indexfiles = ("cgit.cgi") cgi.assign = ("cgit.cgi" => "") mimetype.assign = ( ".css" => "text/css" ) } url.rewrite += ( "^/git(.*)$" => "/cgit/cgit.cgi$1", )
If they aren't already set, give descriptions to each repo by editing the .git/description
file in each.
using cgit
After migrating your repositories to the cgit
server, anywhere downstream should change the remote url like so (from your local mirror):
git remote set-url origin https://your.srvr/git/reponame
If you clone directly from the cgit
instance, the remote will already be set.
To push to the repo, you need ssh access. You will have to change the push remote like so (again, from the local mirror):
git remote set-url --push origin user@your.srvr:/absolute/path/to/the/repo
Then, when you push, you will be asked to supply your SSH password or key passphrase (unless you use a key with no passphrase of course). Worth noting is that with the default settings, cgit
doesn't allow you to push to the branch marked HEAD
on the server, which, if you keep main
checked out on the server, makes it function kind of like a restricted branch on Github. As a shortcut, you can use rsync to push directly to main
if you are sure you are the only one modifying the repository. But you will want to avoid this on projects you are collaborating on, and perhaps use a sudoers rule in /etc/sudoers
to keep guests on your server from using it:
User_Alias GUESTS = alice, bob, charles GUESTS ALL = !/usr/bin/rsync
Of course, it goes without saying that you will want to secure your repo directories with proper group permissions or access control lists to keep private repos private and write access given only to who it's meant for.
and ya done
That's really it. cgit
lacks dedicated features for issue tracking or a documentation/wiki system, but if you really need them in a form more complex than a docs/
folder or BUGS
file, you can either host separate services for them, or, more simply, use dedicated branches or repositories for these features. My cgit instance is hosted at nilfm.cc/git, as you might have guessed from looking at my configs above, and if the setup appeals to you, I encourage you to host an instance yourself!