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
|
# cgit — Configuration Reference
## Configuration File
Default location: `/etc/cgitrc` (compiled in as `CGIT_CONFIG`). Override at
runtime by setting the `$CGIT_CONFIG` environment variable.
## File Format
The configuration file uses a simple `name=value` format, parsed by
`parse_configfile()` in `configfile.c`. Key rules:
- Lines starting with `#` or `;` are comments
- Leading whitespace on lines is skipped
- No quoting mechanism — the value is everything after the `=` to end of line
- Empty lines are ignored
- Nesting depth for `include=` directives is limited to 8 levels
```c
int parse_configfile(const char *filename, configfile_value_fn fn)
{
static int nesting;
/* ... */
if (nesting > 8)
return -1;
/* ... */
while (read_config_line(f, &name, &value))
fn(name.buf, value.buf);
/* ... */
}
```
## Global Directives
All global directives are processed by `config_cb()` in `cgit.c`. When a
directive is encountered, the value is stored in the corresponding
`ctx.cfg.*` field.
### Site Identity
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `root-title` | `"Git repository browser"` | string | HTML page title for the index page |
| `root-desc` | `"a fast webinterface for the git dscm"` | string | Subtitle text on the index page |
| `root-readme` | (none) | path | Path to a file rendered on the site about page |
| `root-coc` | (none) | path | Path to Code of Conduct file |
| `root-cla` | (none) | path | Path to Contributor License Agreement file |
| `root-homepage` | (none) | URL | External homepage URL |
| `root-homepage-title` | (none) | string | Title text for the homepage link |
| `root-link` | (none) | string | `label\|url` pairs for navigation links (can repeat) |
| `logo` | `"/cgit.png"` | URL | Path to the site logo image |
| `logo-link` | (none) | URL | URL the logo links to |
| `favicon` | `"/favicon.ico"` | URL | Path to the favicon |
| `css` | (none) | URL | Stylesheet URL (can repeat for multiple stylesheets) |
| `js` | (none) | URL | JavaScript URL (can repeat) |
| `header` | (none) | path | File included at the top of every page |
| `footer` | (none) | path | File included at the bottom of every page |
| `head-include` | (none) | path | File included in the HTML `<head>` |
| `robots` | `"index, nofollow"` | string | Content for `<meta name="robots">` |
### URL Configuration
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `virtual-root` | (none) | path | Base URL path when using URL rewriting (always ends with `/`) |
| `script-name` | `CGIT_SCRIPT_NAME` | path | CGI script name (from `$SCRIPT_NAME` env var) |
| `clone-prefix` | (none) | string | Prefix for clone URLs when auto-generating |
| `clone-url` | (none) | string | Clone URL template (`$CGIT_REPO_URL` expanded) |
When `virtual-root` is set, URLs use path-based routing:
`/cgit/repo/log/path`. Without it, query-string routing is used:
`?url=repo/log/path`.
### Feature Flags
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `enable-http-clone` | `1` | int | Allow HTTP clone operations (HEAD, info/refs, objects/) |
| `enable-index-links` | `0` | int | Show log/tree/commit links on the repo index page |
| `enable-index-owner` | `1` | int | Show the Owner column on the repo index page |
| `enable-blame` | `0` | int | Enable blame view for all repos |
| `enable-commit-graph` | `0` | int | Show ASCII commit graph in log view |
| `enable-log-filecount` | `0` | int | Show changed-file count in log view |
| `enable-log-linecount` | `0` | int | Show added/removed line counts in log |
| `enable-remote-branches` | `0` | int | Display remote tracking branches |
| `enable-subject-links` | `0` | int | Show parent commit subjects instead of hashes |
| `enable-html-serving` | `0` | int | Serve HTML files as-is from plain view |
| `enable-subtree` | `0` | int | Detect and display git-subtree directories |
| `enable-tree-linenumbers` | `1` | int | Show line numbers in file/blob view |
| `enable-git-config` | `0` | int | Read `gitweb.*` and `cgit.*` from repo's git config |
| `enable-filter-overrides` | `0` | int | Allow repos to override global filters |
| `enable-follow-links` | `0` | int | Show "follow" links in log view for renames |
| `embedded` | `0` | int | Omit HTML boilerplate for embedding in another page |
| `noheader` | `0` | int | Suppress the page header |
| `noplainemail` | `0` | int | Hide email addresses in output |
| `local-time` | `0` | int | Display times in local timezone instead of UTC |
### Limits
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `max-repo-count` | `50` | int | Repos per page on the index (≤0 → unlimited) |
| `max-commit-count` | `50` | int | Commits per page in log view |
| `max-message-length` | `80` | int | Truncate commit subject at this length |
| `max-repodesc-length` | `80` | int | Truncate repo description at this length |
| `max-blob-size` | `0` | int (KB) | Max blob size to display (0 = unlimited) |
| `max-stats` | `0` | int | Stats period (0=disabled, 1=week, 2=month, 3=quarter, 4=year) |
| `max-atom-items` | `10` | int | Number of entries in Atom feeds |
| `max-subtree-commits` | `2000` | int | Max commits to scan for subtree trailers |
| `renamelimit` | `-1` | int | Diff rename detection limit (-1 = Git default) |
### Caching
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `cache-size` | `0` | int | Number of cache entries (0 = disabled) |
| `cache-root` | `CGIT_CACHE_ROOT` | path | Directory for cache files |
| `cache-root-ttl` | `5` | int (min) | TTL for repo-list pages |
| `cache-repo-ttl` | `5` | int (min) | TTL for repo-specific pages |
| `cache-dynamic-ttl` | `5` | int (min) | TTL for dynamic content |
| `cache-static-ttl` | `-1` | int (min) | TTL for static content (-1 = forever) |
| `cache-about-ttl` | `15` | int (min) | TTL for about/readme pages |
| `cache-snapshot-ttl` | `5` | int (min) | TTL for snapshot pages |
| `cache-scanrc-ttl` | `15` | int (min) | TTL for cached scan-path results |
### Sorting
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `case-sensitive-sort` | `1` | int | Case-sensitive repo name sorting |
| `section-sort` | `1` | int | Sort sections alphabetically |
| `section-from-path` | `0` | int | Derive section name from path depth (>0 = from start, <0 = from end) |
| `repository-sort` | `"name"` | string | Default sort field for repo list |
| `branch-sort` | `0` | int | Branch sort: 0=name, 1=age |
| `commit-sort` | `0` | int | Commit sort: 0=default, 1=date, 2=topo |
### Snapshots
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `snapshots` | (none) | string | Space-separated list of enabled formats: `.tar` `.tar.gz` `.tar.bz2` `.tar.lz` `.tar.xz` `.tar.zst` `.zip`. Also accepts `all`. |
### Filters
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `about-filter` | (none) | filter | Filter for rendering README/about content |
| `source-filter` | (none) | filter | Filter for syntax highlighting source code |
| `commit-filter` | (none) | filter | Filter for commit messages |
| `email-filter` | (none) | filter | Filter for email display (2 args: email, page) |
| `owner-filter` | (none) | filter | Filter for owner display |
| `auth-filter` | (none) | filter | Authentication filter (12 args) |
Filter values use the format `type:command`:
- `exec:/path/to/script` — external process filter
- `lua:/path/to/script.lua` — Lua script filter
- Plain path without prefix defaults to `exec`
### Display
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `summary-branches` | `10` | int | Branches shown on summary page |
| `summary-tags` | `10` | int | Tags shown on summary page |
| `summary-log` | `10` | int | Log entries shown on summary page |
| `side-by-side-diffs` | `0` | int | Default to side-by-side diff view |
| `remove-suffix` | `0` | int | Remove `.git` suffix from repo URLs |
| `scan-hidden-path` | `0` | int | Include hidden dirs when scanning |
### Miscellaneous
| Directive | Default | Type | Description |
|-----------|---------|------|-------------|
| `agefile` | `"info/web/last-modified"` | path | File in repo checked for modification time |
| `mimetype-file` | (none) | path | Apache-style mime.types file |
| `mimetype.<ext>` | (none) | string | MIME type for a file extension |
| `module-link` | (none) | URL | URL template for submodule links |
| `strict-export` | (none) | path | Only export repos containing this file |
| `project-list` | (none) | path | File listing project directories for `scan-path` |
| `scan-path` | (none) | path | Directory to scan for git repositories |
| `readme` | (none) | string | Default README file spec (can repeat) |
| `include` | (none) | path | Include another config file |
## Repository Directives
Repository configuration begins with `repo.url=` which creates a new
repository entry via `cgit_add_repo()`. Subsequent `repo.*` directives
modify the most recently created repository via `repo_config()` in `cgit.c`.
| Directive | Description |
|-----------|-------------|
| `repo.url` | Repository URL path (triggers new repo creation) |
| `repo.path` | Filesystem path to the git repository |
| `repo.name` | Display name |
| `repo.basename` | Override for basename derivation |
| `repo.desc` | Repository description |
| `repo.owner` | Repository owner name |
| `repo.homepage` | Project homepage URL |
| `repo.defbranch` | Default branch name |
| `repo.section` | Section heading for grouped display |
| `repo.clone-url` | Clone URL (overrides global) |
| `repo.readme` | README file spec (`[ref:]path`, can repeat) |
| `repo.logo` | Per-repo logo URL |
| `repo.logo-link` | Per-repo logo link URL |
| `repo.extra-head-content` | Extra HTML for `<head>` |
| `repo.snapshots` | Snapshot format mask (space-separated suffixes) |
| `repo.snapshot-prefix` | Prefix for snapshot filenames |
| `repo.enable-blame` | Override global enable-blame |
| `repo.enable-commit-graph` | Override global enable-commit-graph |
| `repo.enable-log-filecount` | Override global enable-log-filecount |
| `repo.enable-log-linecount` | Override global enable-log-linecount |
| `repo.enable-remote-branches` | Override global enable-remote-branches |
| `repo.enable-subject-links` | Override global enable-subject-links |
| `repo.enable-html-serving` | Override global enable-html-serving |
| `repo.enable-subtree` | Override global enable-subtree |
| `repo.max-stats` | Override global max-stats |
| `repo.max-subtree-commits` | Override global max-subtree-commits |
| `repo.branch-sort` | `"age"` or `"name"` |
| `repo.commit-sort` | `"date"` or `"topo"` |
| `repo.module-link` | Submodule URL template |
| `repo.module-link.<submodule>` | Per-submodule URL |
| `repo.badge` | Badge entry: `url\|imgurl` or just `imgurl` (can repeat) |
| `repo.hide` | `1` = hide from listing (still accessible by URL) |
| `repo.ignore` | `1` = completely ignore this repository |
### Filter overrides (require `enable-filter-overrides=1`)
| Directive | Description |
|-----------|-------------|
| `repo.about-filter` | Per-repo about filter |
| `repo.commit-filter` | Per-repo commit filter |
| `repo.source-filter` | Per-repo source filter |
| `repo.email-filter` | Per-repo email filter |
| `repo.owner-filter` | Per-repo owner filter |
## Repository Defaults
When a new repository is created by `cgit_add_repo()`, it inherits all global
defaults from `ctx.cfg`:
```c
ret->section = ctx.cfg.section;
ret->snapshots = ctx.cfg.snapshots;
ret->enable_blame = ctx.cfg.enable_blame;
ret->enable_commit_graph = ctx.cfg.enable_commit_graph;
ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
ret->enable_subject_links = ctx.cfg.enable_subject_links;
ret->enable_html_serving = ctx.cfg.enable_html_serving;
ret->enable_subtree = ctx.cfg.enable_subtree;
ret->max_stats = ctx.cfg.max_stats;
ret->max_subtree_commits = ctx.cfg.max_subtree_commits;
ret->branch_sort = ctx.cfg.branch_sort;
ret->commit_sort = ctx.cfg.commit_sort;
ret->module_link = ctx.cfg.module_link;
ret->readme = ctx.cfg.readme;
ret->about_filter = ctx.cfg.about_filter;
ret->commit_filter = ctx.cfg.commit_filter;
ret->source_filter = ctx.cfg.source_filter;
ret->email_filter = ctx.cfg.email_filter;
ret->owner_filter = ctx.cfg.owner_filter;
ret->clone_url = ctx.cfg.clone_url;
```
This means global directives should appear *before* `repo.url=` entries, since
they set the defaults for subsequently defined repositories.
## Git Config Integration
When `enable-git-config=1`, the `scan-tree` scanner reads each repository's
`.git/config` and maps gitweb-compatible directives:
```c
if (!strcmp(key, "gitweb.owner"))
config_fn(repo, "owner", value);
else if (!strcmp(key, "gitweb.description"))
config_fn(repo, "desc", value);
else if (!strcmp(key, "gitweb.category"))
config_fn(repo, "section", value);
else if (!strcmp(key, "gitweb.homepage"))
config_fn(repo, "homepage", value);
else if (skip_prefix(key, "cgit.", &name))
config_fn(repo, name, value);
```
Any `cgit.*` key in the git config is passed directly to the repo config
handler, allowing per-repo settings without modifying the global cgitrc.
## README File Spec Format
README directives support three forms:
| Format | Meaning |
|--------|---------|
| `path` | File on disk, relative to repo path |
| `/absolute/path` | File on disk, absolute |
| `ref:path` | File tracked in the git repository at the given ref |
| `:path` | File tracked in the default branch or query head |
Multiple `readme` directives can be specified. cgit tries each in order and
uses the first one found (checked via `cgit_ref_path_exists()` for tracked
files, or `access(R_OK)` for disk files).
## Macro Expansion
The `expand_macros()` function (in `shared.c`) performs environment variable
substitution in certain directive values (`cache-root`, `scan-path`,
`project-list`, `include`). A `$VARNAME` or `${VARNAME}` in the value is
replaced with the corresponding environment variable.
## Example Configuration
```ini
# Site settings
root-title=Project Tick Git
root-desc=Source code for Project Tick
logo=/cgit/cgit.png
css=/cgit/cgit.css
virtual-root=/cgit/
# Features
enable-commit-graph=1
enable-blame=1
enable-http-clone=1
enable-index-links=1
snapshots=tar.gz tar.xz zip
max-stats=quarter
# Caching
cache-size=1000
cache-root=/var/cache/cgit
# Filters
source-filter=exec:/usr/lib/cgit/filters/syntax-highlighting.py
about-filter=exec:/usr/lib/cgit/filters/about-formatting.sh
# Scanning
scan-path=/srv/git/
section-from-path=1
# Or manual repo definitions:
repo.url=myproject
repo.path=/srv/git/myproject.git
repo.desc=My awesome project
repo.owner=Alice
repo.readme=master:README.md
repo.clone-url=https://git.example.com/myproject.git
repo.snapshots=tar.gz zip
repo.badge=https://ci.example.com/badge.svg|https://ci.example.com/
```
|