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"
}
}
Copyright (c) 2026 Joachim Wiberg <troglobit@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.