Digit — Can You Dig It?
Digit (display git) is a fast, simple web frontend to your git repositories, made to resemble other popular interfaces.
- Browse repos, files, trees, and commits
- Mirror-tree aware: repos nested
host/org/repo.gitgroup per org - File tree view with mode, size, and per-directory readmes
- Blame view with per-commit line groups
- Commit log with full diff output, and message search
- Patch view, append
.patchto any commit URL - Tarball and zip downloads of any branch or tag
- Read-only clone over smart HTTP, with a GitHub-style clone menu
- Atom and RSS commit feeds, per repo and site-wide
- Tag and branch listing
- Syntax highlighting, line anchors with visual range marks
- Keyboard shortcuts for commit and log navigation (press
?for the list) - GitHub Markdown rendering, with emoji, tables, alerts, and inline images
- Readme quick-links: license, contributing, code of conduct, security
- Templated HTML (fully customizable)
- Dark & Light mode, auto by default with a navbar toggle
- Configurable navbar, per-repo upstream and website links
- Built-in caching: ETag revalidation, immutable hash-pinned URLs
Screenshots
Repo overview
|
Tree view
|
Visual line marks
|
Repo viewer
|
Configuring
Digit looks for config.yaml in the current directory, falling back
to /etc/digit.yaml; use --config <path> to point elsewhere.
repo:
scanPath: /srv/git
readme:
- README.md
- README
mainBranch:
- main
- master
ignore:
- foo
unlisted:
- private-repo
dirs:
templates: ./templates
static: ./static
meta:
favicon: favicon.svg
title: Digit
description: Explore available Git repositories
syntaxHighlight: default
features:
blame: true
archive: true
feeds: true
clone: false
search: false
nav:
home: https://example.com
help: https://github.com/troglobit/digit
links:
- name: GitHub
url: https://github.com/example
server:
name: git.example.com
ssh: git@git.example.com
host: 127.0.0.1
port: 3000
| Field | Description |
|---|---|
repo.scanPath |
Directory containing repos, nested up to host/org/repo.git |
repo.readme |
Readme filenames to look for (first match wins) |
repo.mainBranch |
Branch names to try as default branch |
repo.ignore |
Repos to exclude entirely (404); * globs one path segment |
repo.unlisted |
Repos to hide from the index (still accessible by URL); globs too |
dirs.templates |
Path to custom Go html/template files |
dirs.static |
Path to custom static assets (CSS, images) |
meta.syntaxHighlight |
Client-side syntax highlighting (highlight.js); empty = disabled |
features.blame |
Blame view; disable to save CPU on large files (default on) |
features.archive |
Tarball downloads; disable to save bandwidth (default on) |
features.feeds |
Atom/RSS feeds: /atom, /repo/atom[/ref], rss alike (default on) |
features.clone |
Read-only smart HTTP clone (default off) |
features.search |
Log search, case-insensitive and verbatim (default off) |
nav.home |
Navbar brand link, e.g. your homepage (default /) |
nav.help |
Navbar help link (default: digit's project page) |
nav.links |
Extra navbar links (name + url), right-aligned |
server.name |
Public host name, for absolute feed links and clone URLs |
server.ssh |
SSH clone prefix, e.g. git@git.example.com; adds SSH to the menu |
server.host |
Listen address (default 127.0.0.1 — see reverse proxy below) |
server.port |
Listen port (default 3000) |
Mirrored repositories
Repos may live directly in scanPath or nested like a mirror tree,
e.g. <host>/<org>/<repo>.git, and both styles can be mixed. Nested
repos get URLs matching their path (/github.com/troglobit/mg/), the
overview groups them per org, and every level has its own index page
(/github.com/, /github.com/troglobit/). With more than 20 repos
the front page lists orgs with repo counts instead of every repo.
To hide a whole org, glob it:
repo:
ignore:
- github.com/some-vendor/*
Per-repository metadata
The repo overview and summary pages show the classic gitweb
description file, so set it in each repo:
$ echo "Fast init for Linux systems. Cookies included." > /srv/git/finit.git/description
To link a repo to its upstream, e.g. the GitHub project it mirrors,
or to its website, set the digit.upstream and digit.website keys
in the repo's git config. They show up as upstream ↗ and
website ↗ in the repo navigation:
$ git -C /srv/git/finit.git config digit.upstream https://github.com/troglobit/finit
$ git -C /srv/git/finit.git config digit.website https://troglobit.com/projects/finit/
Building
After cloning this repo, build a stand-alone binary with:
$ cd digit/
$ make
The Makefile is a thin wrapper around the Go toolchain (go build);
make check vets and tests, make fmt formats. No external
dependencies beyond the Go toolchain.
At runtime digit requires git to be installed on the server — all
repository access shells out to git plumbing commands. This is the
same requirement as Gogs, and what keeps CPU usage low: the git binary
uses commit-graph and pack index optimizations that pure-Go git
libraries lack.
Installation
make install installs the binary, a sample /etc/digit.yaml (an
existing one is left untouched), and the bundled systemd service,
which runs digit as a restricted git user. prefix, sysconfdir,
unitdir, and DESTDIR are honored, for packaging:
$ sudo make install
$ sudo useradd --system --home /srv/git --shell /usr/sbin/nologin git
Point repo.scanPath in /etc/digit.yaml at your repos — they must
be readable by the git user, but need not be owned by it: digit
marks everything under scanPath as safe.directory for its own
git commands, so mirrors synced by another user work out of the box,
without tripping git's dubious-ownership check. Then:
$ sudo systemctl daemon-reload
$ sudo systemctl enable --now digit
Cloning
With features.clone enabled, every repo can be cloned read-only
over smart HTTP — handy for an office-internal digit:
$ git clone https://git.example.com/finit
Digit proxies the two upload-pack endpoints to git http-backend;
push is never routed, so repos cannot be written to. Set
server.name and repo pages get a clone menu next to the branch
selector, with copyable URLs and a tarball download, plus
go-import meta tags so Go vanity imports work.
If the server also runs sshd with git access, set server.ssh to
the user@host prefix and the menu offers SSH clone URLs too, built
from each repo's real on-disk path.
Logging and crawlers
Under systemd digit detects the journal and drops its own
timestamps. Under other init systems, e.g. Finit, start digit
with -s/--syslog to log via syslog(3) instead of stderr.
Rejected requests are logged with the client address:
client 192.0.2.7: GET /finit/nope: 404
which fail2ban can match to ban scanners and scrapers:
# /etc/fail2ban/filter.d/digit.conf
[Definition]
failregex = ^.*client <HOST>: .*: (404|500)
journalmatch = _SYSTEMD_UNIT=digit.service
Digit also serves a /robots.txt keeping well-behaved crawlers off
the expensive endpoints (archive, blame, raw); override it by
placing your own in dirs.static.
Running behind a reverse proxy
Digit listens on 127.0.0.1:3000 by default, made to sit behind a
TLS-terminating reverse proxy. All generated links are root-relative
— the listen address never leaks into any page — so no rewriting is
needed. The one requirement is that digit is served at the root of
its host: use a dedicated (sub)domain like git.example.com; mounting
under a sub-path such as example.com/git/ is not supported.
With nginx:
server {
listen 80;
server_name git.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
With merecat httpd, using virtual hosts:
virtual-host = true
server https {
port = 443
certfile = /etc/letsencrypt/live/example.com/fullchain.pem
keyfile = /etc/letsencrypt/live/example.com/privkey.pem
proxy-pass "/**" {
host = "git.example.com"
backend = "http://127.0.0.1:3000"
}
}