1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
|
cgit - CGI for Git
==================
This is an attempt to create a fast web interface for the Git SCM, using a
built-in cache to decrease server I/O pressure.
Source tree
-----------
* source/ is the C code, built inside the Git tree in vendor/git, and assets/
holds the static files make install copies.
* custom/ holds the reference cgitrc, drop-in server configs in servers/, the
Lua filters in extensions/ and in hooks/ standalone post-receive hooks that
keep the idle column, the cache and the commit-graph current.
* tools/ holds build-release.sh for a hardened build, without Lua unless given
lua, build-development.sh for a debug build with sanitizers, and serve.py,
which previews the built binary on a local port and needs a cgitrc, ./cgitrc
unless --config names another.
* tests/ is the suite make test runs.
Installation
------------
Building cgit involves building a proper version of Git. How to do this depends
on how you obtained the cgit sources.
The build needs GNU make, run as gmake on the BSDs, a C compiler and the zlib
headers, and make get-git below also needs curl or wget, and xz. zlib is the
only library cgit requires, so no development packages for OpenSSL, libcurl,
gettext or Tcl are needed. On Debian and Ubuntu that is one command.
$ sudo apt install make gcc zlib1g-dev curl xz-utils
In a cloned cgit repository, first fetch the Git submodule.
$ git submodule update --init
From a cgit tarball, download a proper git version instead.
$ make get-git
With the Git tree in place, build and install cgit.
$ make
$ sudo make install
This installs cgit.cgi, cgit.css, cgit.js, cgit.png, favicon.ico and robots.txt
into /var/www/htdocs/cgit, and the Lua filters from custom/extensions into
/usr/local/lib/cgit/filters. The server configs in custom/servers/ look for the
binary in /usr/lib/cgit and the other five files in /usr/share/cgit instead. To
install there, put these two lines in a file named cgit.conf next to the
Makefile before running make install.
CGIT_SCRIPT_PATH = /usr/lib/cgit
CGIT_DATA_PATH = /usr/share/cgit
The Makefile lists the other variables cgit.conf can set, prefix among them.
make install runs the build again first, with the variables it is given, so it
needs the same ones as the build. To install the hardened binary that
tools/build-release.sh built rather than a default build, skip that rebuild with
make's -o option.
$ sudo make -o all install
On a musl system such as Alpine, Git's code also needs the Linux kernel headers,
packaged there as linux-headers, and its bundled regex in place of musl's, which
lacks REG_STARTEND. make install and make test take the same setting.
$ make NO_REGEX=YesPlease
Lua is optional and only powers the lua: filter extensions, the filters in
custom/extensions/. A plain build auto-detects a Lua through pkg-config,
preferring LuaJIT, and falls back to a Lua-less binary when none is found. On
Debian and Ubuntu, detection needs pkg-config and a development package such as
libluajit-5.1-dev or liblua5.4-dev. To pin an implementation, name its
pkg-config module.
$ make LUA_PKGCONFIG=lua5.4
Acceptable LUA_PKGCONFIG values include "luajit", "lua", "lua5.5", "lua5.4",
"lua5.3", "lua5.2" and "lua5.1". To build without Lua, so the binary needs no
Lua at runtime, turn it off.
$ make NO_LUA=1
A binary built this way still runs every exec: filter, but a filter named with
the lua: prefix has no interpreter to run in, so it is refused with an error on
every page until the setting is taken out of cgitrc. make test builds this
variant as well, into build/nolua, and checks that it serves pages, runs exec
filters and refuses lua ones, while the rest of the suite runs the full build.
Quick start
-----------
cgit is a CGI program that reads its options and its list of repositories from
/etc/cgitrc. custom/cgitrc is a fully commented starting point with every option
at its default, and MANUAL.txt documents each option in full. With cgit
installed as above, copy custom/cgitrc to /etc/cgitrc and point it at the
repositories, with scan-path for every repository below one directory or a
repo.url and a repo.path line for each. They have to be readable by the account
the web server runs cgit as, www-data on Debian, since a scanned repository it
cannot read is left off the index with only a line in the error log.
custom/servers/ holds a short config for each of nginx, Apache and lighttpd
that serves cgit over plain HTTP at the root of git.example.org, with HTTPS
and git-http-backend for smart HTTP clone and push as commented examples.
Their headers say what each needs and which paths it assumes. Change the host
name and include the one for your server from its main config.
The cache is off until cache-size is set above 0, as in cache-size=1000 placed
above scan-path. Its directory has to exist first, owned by the same account
and closed to the others.
$ sudo install -d -o www-data -m 0700 /var/cache/cgit
From a shell, --version names the build and whether it has Lua. --nohttp
--query= prints the page for a query string, reading /etc/cgitrc or the file
CGIT_CONFIG names, and --scan-path= prints what a scan of a directory finds as
cgitrc lines, without reading cgitrc. Run as the web server's account, a page
meets its permissions and prints on stderr what the error log would get.
$ /usr/lib/cgit/cgit.cgi --version
$ sudo -u www-data /usr/lib/cgit/cgit.cgi --nohttp --query=url=demo/log
Upgrading from upstream cgit
----------------------------
An upstream cgitrc still loads, but cgit skips the old name of a renamed option
with only an "Unknown config key" line in the error log, so an old
noplainemail=1 shows email addresses again. These are the new names.
agefile age-file
cache-repo-ttl cache-summary-ttl
cache-root-ttl cache-index-ttl
cache-scanrc-ttl cache-scan-ttl
noheader=1 enable-header=0
noplainemail=1 enable-plain-email=0
renamelimit rename-limit
repo.extra-head-content repo.head-content
In a scanned repository's own cgitrc, extra-head-content is now head-content.
enable-filter-overrides is gone, as repo.*-filter lines always apply and a
scanned repository's own filters wait on trust-scan-config. owner-filter,
repo.owner-filter and repo.homepage are gone with nothing in their place.
Customisation
-------------
Every page is one document with the same skeleton, so a site stylesheet or
script added through the css and js options has stable hooks to work with.
* The root is div#cgit. It carries data-page with the page name, summary, log,
tree and so on, and data-repo with the repository url on repository pages,
so a rule can target one page or one repository. In embedded mode it also
carries the class cgit-embedded.
* Inside it sit header#header, nav.tabs with the search form, nav.path for the
breadcrumb, main.content and footer.footer. Each nav, listing and option form
carries an aria-label, and the active tab and the current pager link carry
aria-current.
* Every listing is a table.list with a second class naming it, repolist,
summary, refs, log, tree or clone, the last holding the clone urls of an
empty repository. Each section of a listing is a tbody named repos,
branches, tags, log, clone or tree, and each row carries its kind, repo,
branch, tag, commit, dir, blob, link or mod. A file row's link also carries
ext- followed by the file extension.
* The commit and tag pages use table.commit-info, the trailer table is
table.commit-trailers, the diffstat is table.diffstat and each file of a diff
is div.file, or tbody.file in the side-by-side view, with the path in
data-path. The blob and blame pages open with div.blob-header.
* Relative ages are time elements with an age-* class and the timestamp in
data-ut, while a date shown in full is a plain time element. Line numbers are
anchors named n followed by the line, while enable-tree-linenumbers is on.
A character in a file that a browser would start a new line at, though git
does not, such as a carriage return with no line feed after it, is shown as
an escape sequence in span.escape.
* The bundled auth filters write their login form as form.login. A
p.login-error above it reports a failed login, a signed-in visitor without
access to the repository or a page served without https, and stands alone
when logins are unavailable.
* Colours, fonts and metrics are custom properties on div#cgit, so a theme can
redefine those alone. Every rule in cgit.css starts with div#cgit, apart from
the background and margins it sets on html.cgit-standalone, the class of the
document cgit writes when it is not embedded. A rule of your own needs the
div#cgit prefix to win.
* The script exposes window.cgit.updateAges and window.cgit.highlightLines for
a page that changes the rows or the lines after load.
A theme that only wants a colour change is a few lines.
div#cgit {
--link: light-dark(#7a1f1f, #f0a0a0);
--font-sans: Georgia, serif;
}
div#cgit[data-page='log'] table.list tr.commit:hover {
background: var(--surface);
}
Keep a theme in a file of its own, since make install overwrites cgit.css. The
first css line replaces the default rather than adding to it, as a js line does
cgit.js, so list cgit.css first. The server configs in custom/servers/ serve
only the five shipped files from /usr/share/cgit and send every other url to
cgit, so put the theme there and add its name to their list.
css=/cgit.css
css=/theme.css
tests/t0005-markup.sh checks that every hook named here is still emitted.
Filter extensions
-----------------
The optional Lua filters in custom/extensions/ mostly need extra Lua modules,
and each script's header lists the exact install commands for its own.
* The auth filters, auth-file.lua and auth-inline.lua, need luaossl and
luaposix.
* The email filters, email-gravatar.lua and email-libravatar.lua, need luaossl.
* The syntax highlighter, syntax-highlight.lua, needs lpeg and a Scintillua
recent enough to have detect().
* The about-page renderer, about-render.lua, needs lpeg for markdown and man
pages. Plain text needs only Lua.
* link-commits.lua and link-trailers.lua need nothing beyond Lua itself.
On Lua 5.5 the auth and email filters need luaossl 20260910 or later, or a build
patched for 5.5 such as Alpine's, since earlier releases fail to load there.
The auth filters create the secret that signs their cookies on first use, as
/var/lib/cgit/auth-secret, but not its directory. Make that by hand, owned by
the account the web server runs cgit as, with mode 0700.
Urls and query parameters
-------------------------
cgit serves every page from one CGI program, so a url names the repository, the
page and a path inside that page. There are two forms and they carry the same
information. The one cgit's own links take is a path, written as
/<repo>/<page>/<path> with anything else as a query string. In the other the
whole request lives in the query string as ?url=<repo>/<page>/<path>.
The page name is the second element and is one of about, atom, blame, blob,
commit, diff, log, patch, plain, rawdiff, refs, snapshot, stats, summary, tag or
tree. Leaving it out gives the repository summary, and leaving the repository
out as well gives the index of repositories. Everything after the page name is
the path argument, so /demo/tree/src/main.c asks for the tree page at
src/main.c. A repository whose name contains a character that is special in a
url has it percent-encoded, and a plus sign has to be written %2b because a bare
plus decodes to a space.
A page of the site itself has no repository in front of it, so p names it
instead, as in ?p=about. These pages are about, which shows root-readme,
repolist, the index under its own name, login, which an auth filter answers, and
ls_cache, which lists the cache when enable-cache-list allows it.
Every parameter is optional. The pages column names the pages that read each
one, where a value of all marks the few not tied to particular pages.
+----------+----------------------+--------------------------------------------+
| param | pages | description |
+----------+----------------------+--------------------------------------------+
| url | all | repository, page and path in one value |
| r | all | the repository on its own, in place of url |
| p | all | the page on its own, in place of url |
| h | all | the branch or ref to read |
| id | all | pin the page to one commit or object |
| id2 | diff patch rawdiff | second object of a diff, paired with id |
| ofs | index log refs | offset into a paged listing |
| ofs | stats | authors to show, -1 for all of them |
| path | all | restrict the page to one path |
| q | index log | the search term, matched literally |
| qt | log | one of grep, author, committer or range |
| s | index | sort key for the listing |
| showmsg | log | show full commit messages |
| period | stats | the statistics window, one of w, m, q or y |
| dt | diff commit | 0 unified, 1 side-by-side or 2 stat only |
| ss | diff commit | shorthand for the side-by-side diff |
| all | atom | include every ref rather than one branch |
| context | diff commit | lines of context around a diff hunk |
| ignorews | diff commit | ignore whitespace when diffing |
| follow | log | follow a single path across renames |
+----------+----------------------+--------------------------------------------+
A number out of range is clamped rather than refused, ofs to 100000 and context
to 40 lines, and q is cut at 512 characters.
A few endpoints are not ordinary pages. The snapshot page takes a filename
rather than a ref, so /demo/snapshot/demo-1.0.tar.gz names both the ref and the
archive format through the suffix, and the formats on offer are set by the
snapshots option. The plain page serves a blob as its own bytes under headers
that stop a browser treating repository content as markup. The atom page is a
feed rather than a page and accepts h, path and all. The clone endpoints under
info and objects implement the dumb HTTP protocol and are only present when http
clone is enabled.
A url fragment can address lines inside a file. While enable-tree-linenumbers is
on, as it is by default, the tree and blame pages give every line an anchor
named n followed by its number, so #n17 jumps to line 17 and highlights it, and
a range written #n17-n24 highlights the whole run and scrolls it into view.
Some worked examples, in the url= form.
?url= the repository index
?url=demo summary for the demo repository
?url=demo/log&h=next log of the next branch
?url=demo/log&qt=author&q=alice commits authored by alice
?url=demo/tree/src&h=v1.0 the src directory at tag v1.0
?url=demo/commit&id=v1.0 one commit, pinned to a tag
?url=demo/diff&id=main&id2=next diff between two branches
?url=demo/plain/README.md the raw bytes of one file
?url=demo/atom&h=main the commit feed for a branch
Testing
-------
make test builds cgit, the variant without Lua and the whole bundled Git tree,
whose test library the suite is built on, then runs every script in tests/. It
carries on past a failing script and ends with one line summing up the run,
with the skipped tests counted apart from the passes, and the names of the
scripts that failed or were skipped whole. Once built, make -C tests runs the
suite again without building, and a script runs on its own from tests/.
$ make test
$ cd tests && sh t0101-index.sh -v
The scripts take the options of Git's test library, such as -v to show what
each test does and prints, -i to stop at the first failure and --run to pick
tests by number, and make test passes them on through CGIT_TEST_OPTS. A failing
test shows only its name and its commands, so run its script again with -v to
see why it failed. --valgrind runs cgit, and only cgit, under Valgrind's
memcheck, and --valgrind=<tool> under another tool. The trash directories need
a local filesystem, as the cache tests rely on flock locks, which NFS can only
emulate.
make test builds with the flags on its own command line, so after
tools/build-development.sh a plain make test would rebuild and test an ordinary
binary. Have the script run the suite instead, as it passes its arguments on to
make.
$ ./tools/build-development.sh test
The suite needs perl and a POSIX shell. Tests that need more are skipped where
the tool is missing. These are tidy 5 or later, strace with the file class of
calls, python3 3.8 or later, xmllint, unzip, gzip, bzip2, lzip, xz and zstd, a
standalone Lua or LuaJIT for the unit checks of the filters, with lpeg, luaossl
and luaposix for some of them, and dash, ksh and bash besides sh for the hooks.
A few need an account other than root.
A run that has to cover everything, such as one before a release, names the
prerequisites it cannot go without in CGIT_TEST_REQUIRE, separated by spaces,
and a script that finds one missing then stops with an error rather than skip
the tests that need it. The names are CGIT_LUA for a cgit built with Lua,
CGIT_NOLUA for build/nolua, TIDY, STRACE and STRACE_DELAY, PYTHON, XMLLINT,
UNZIP, GZIP, BZIP2, LZIP, XZ, ZSTD, SANITY for an account other than root,
SUBMODULE for a vendor/git cloned as a submodule, and the others the tests in a
script name, such as CGIT_LUA_LPEG.
$ make test CGIT_TEST_REQUIRE="CGIT_LUA TIDY STRACE SANITY"
Credits
-------
cgit was created by Lars Hjemli and maintained upstream for many years, most
recently by Jason A. Donenfeld. This repository is an independent fork that
builds on their work. They are not maintainers of this fork and are not
responsible for it.
cgit is built on Git, which Linus Torvalds created and Junio C Hamano maintains
with the help of many contributors. Git's own code, bundled in vendor/git, is
compiled into cgit and reads every repository that cgit shows.
|