blob: 53c9eb29e3c48b148d2e475ceb2902084cfba2ba (plain)
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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
cgit - Changelog
================

Notable changes to this fork, newest release first. History before v2.0.0
belongs to upstream cgit and is not covered here.


v2.7.0 (2026-09-08)
-------------------

A configuration release. The badly named keys are renamed, the statistics switch
folds back into max-stats, the parser now reports what it ignores, and the theme
toggle is gone.

Added
.....

* Unknown or misplaced configuration keys are reported to stderr instead of
  being dropped silently.

Changed
.......

* noheader became enable-header, with its polarity flipped.
* noplainemail became enable-plain-email, with its polarity flipped.
* cache-root-ttl became cache-index-ttl.
* cache-repo-ttl became cache-summary-ttl.
* cache-scanrc-ttl became cache-scan-ttl.
* agefile became age-file.
* renamelimit became rename-limit.
* repo.extra-head-content became repo.head-content.
* Old key spellings are not read and draw the unknown-key warning.
* max-stats enables the statistics page and bounds its coarsest period again, as
  it did before v2.2.0.
* enable-stats and repo.enable-stats are gone.
* repo.max-stats=0 turns the statistics page off for one repository.

Removed
.......

* The header theme toggle. The light and dark theme follows the system
  preference alone, and a saved choice in localStorage is no longer read.
* Selects no longer submit their form the moment they change. The diff and stats
  panels always show their reload button instead of keeping it for readers
  without scripting.

Fixed
.....

* A lua filter that fails now answers with an error page naming the script and
  the error, where before the page was fed back into the broken filter and the
  visitor got an empty response.


v2.6.1 (2026-09-01)
-------------------

A small release. Every clone row is now a link a browser can follow, and the
diffstat graph drops the output's last inline style, so a strict
Content-Security-Policy needs no exceptions.

Changed
.......

* Clone rows are links. The ssh, scp and git forms cannot be followed by a
  browser, so those rows point at the first http url in the clone list, or at
  the repository page when there is none, and the scp form stays out of the
  document head since it is not a URI.
* The diffstat bars take their width from colspan on a fixed-layout table rather
  than from inline styles. A site pointing css= at a stylesheet of its own needs
  the new inner graph table rule from cgit.css, or the bars render as equal
  width segments spaced apart.


v2.6.0 (2026-08-28)
-------------------

A cleanup release. Output is ASCII only and deterministic, the statistics page
drops its language breakdown, and two bugs a cross-compiler review found are
fixed.

Added
.....

* README.txt documents how cgit urls are built, both url forms, every query
  parameter, the endpoints that are not pages, and worked examples.
* tests/README.txt describes the suite layout, numbering and traps.
* Pages carry a meta description, from the repository or root description.
* Submodule links resolve through .gitmodules, and submodule rows are set apart
  in the tree listing.

Changed
.......

* Output is ASCII only. The truncation marker is an ellipsis, the title path
  separator is plain, and unrepresentable bytes print as a question mark.
* Non-breaking space padding is done with CSS.
* Pages are byte for byte deterministic. The footer drops its clock and an empty
  atom feed is dated at the epoch.
* The charset is spelled UTF-8 everywhere.
* Snapshots use registered media types, so application/gzip, application/zip and
  application/zstd replace their x- forms.
* Status lines use the standard HTTP reason phrases.
* Table header and image attributes follow the usual order.
* Test scripts are renumbered into themed ranges and prefer POSIX forms.

Removed
.......

* The statistics page no longer breaks the tree down by language. The built-in
  extension table was a standing maintenance cost, classifying by extension
  misleads, and the tree walk it needed was the only part of the page reading
  object sizes. The commits-per-author table is unchanged.

Fixed
.....

* The statistics page no longer reads past its argument list when given a path.
  The array handed to git's revision setup lacked a trailing null, so a url such
  as /repo/stats/dir read whatever followed it on the stack.
* A detached head stays selected in the branch switcher. Browsing at a raw
  commit or tag left it resting on the first branch, so switch moved away.


v2.5.0 (2026-08-23)
-------------------

A correctness and conformance release. The generated markup moves fully to
HTML5, cgit stops emitting HTTP headers a front-end server should own, the page
cache and repository scan get real locking, and pages pinned to a commit now say
so.

Added
.....

* Pages pinned to a commit via the id parameter show it. The branch switcher
  gains a "(detached)" entry, the page title carries the short hash, and the
  path strip gains a rev crumb linking back to the branch tip.
* The shipped server configs gain Permissions-Policy, cross-origin isolation
  headers and form-action in the CSP, and enable HSTS. The nginx config adds
  catch-all blocks that drop requests for hostnames the site does not serve,
  since cgit keys its cache on Host.
* Operators get log lines for two previously silent failures, a cache too small
  for its keys and a repository scan lock that fails for reasons other than
  contention.
* The documentation now spells out that the cache directory must be pre-created,
  owned by the web server account and mode 0700.
* Login forms carry autocomplete hints for password managers, and avatar images
  load lazily.

Changed
.......

* Atom feeds conform to the RFC. They are served as application/atom+xml with an
  XML declaration, invalid bytes are replaced instead of emitted, and every
  entry carries an author name.
* Dates render as HTML time elements with strict ISO 8601 values.
* Markup is fully HTML5. Complete documents everywhere, real table header cells,
  no XHTML self-closing slashes and no HTML comments in the output. The plain
  directory listing gets a doctype so browsers leave quirks mode.
* cgit no longer sends Last-Modified, Expires or ETag. Front-end caching policy
  lives in the server configs, which now append headers rather than replace
  cgit's own.
* Only a full object id qualifies for cache-static-ttl, since the id parameter
  accepts any rev git can resolve.
* Percentages in the diffstat bars and language table print from integers, so a
  filter that switches the numeric locale cannot corrupt the output.
* URL encoding is tightened. Ampersands and plus signs are percent-encoded in
  paths, and query strings are escaped at each sink instead of being
  pre-escaped.
* The responsive breakpoints only hide or restack chrome and never change font
  sizes or gutters. Narrow screens drop the search box, branch switcher and
  author columns, and diff tables scroll inside their own box.
* The client-side age refresher uses the same bucket arithmetic as the server,
  so refreshed ages no longer drift from rendered ones.
* The example agefile hook was renamed to post-receive.cgit-age.

Removed
.......

* The repository homepage feature, including repo.homepage, the gitweb.homepage
  mapping and the homepage tab.
* The post-update.update-server-info example hook.

Fixed
.....

* Error pages are no longer cached, so requesting a commit before it is pushed
  can no longer pin a 404 into the cache forever.
* An abandoned cache fill no longer appends a second page to the response,
  publishes its lock file or leaves a stale copy to be served.
* A crashed repository scan no longer freezes the index forever. Scan and cache
  locks are fcntl locks released by the kernel with the process, and lock
  holders re-verify their lock file after acquiring it, closing a race that
  could truncate a live file.
* Blobs containing a NUL byte anywhere are treated as binary in tree and blame
  views, and the raw path substitutes replacement characters instead of
  truncating at the NUL.
* An annotated tag whose tagger has no email no longer passes NULL to the email
  filter, repositories without an owner no longer render an empty link,
  single-page logs drop the pager, and filler rows use correct colspan values.
* The dev preview server splits CGI headers at the earliest blank line, so DOS
  line endings in page output no longer swallow the document head.


v2.4.0 (2026-08-08)
-------------------

A performance and robustness release. Output is buffered instead of written
fragment by fragment, the hot paths shed repeated work, and a cluster of fixes
closes crashes reachable from repository-controlled content.

Changed
.......

* The bundled Git is updated from 2.54.0 to 2.55.0, with NO_RUST set so Cargo
  does not become a silent build requirement.
* Page output is collected in a 64 KB buffer and written in whole blocks instead
  of one write per HTML fragment.
* Blame and tree line numbers, hex dump rows and intra-line diff highlights are
  emitted in batches rather than per line or character.
* The diff view renders each file while it is already open and replays it after
  the diffstat, replacing a second full tree walk.
* Blame reuses a commit's rendered detail across its entries, the index resolves
  repository ages once before sorting, and the stats page computes its period
  labels once.
* Side-by-side tab expansion is a single pass, where it was previously quadratic
  on tab-heavy lines.
* Search queries are clamped to 512 characters, bounding the matching work one
  request can demand and the size of cache keys.
* The sources compile as gnu17 so their meaning does not drift with compiler
  defaults, and the release script probes each hardening flag, preferring
  FORTIFY_SOURCE level 3.

Fixed
.....

* A repository under scan-path could supply its own module-link template, which
  was handed straight to printf. Surplus conversions could crash cgit or read
  stack memory into the page. Templates are now expanded manually.
* Sorting branches by age crashed when a branch ref pointed at a tag or tree
  object.
* Hunks whose header omits a length, as git writes for single-line hunks, were
  numbered from zero in side-by-side diffs and lost their links.
* A request whose cache key was too long to read back could never hit,
  regenerating the page every time while still writing an unusable slot. Such
  requests now skip the cache and log it.
* Git config isolation ran from a constructor attribute that some compilers
  silently discard. It now runs from main.
* A memory leak of deferred side-by-side lines and an integer-truncation bug in
  the stats author ordering.


v2.3.0 (2026-08-05)
-------------------

A consolidation release focused on how the project is laid out, deployed and
operated. Everything an operator ships or edits now lives under custom/, the
server configs became complete standalone files, and the hooks and auth filters
were hardened.

Added
.....

* New enable-relative-dates option, default 1. Set to 0 to always show calendar
  dates in the age columns instead of elapsed times.
* New date-format option controlling those calendar dates, accepting git's date
  format names plus strftime formats.
* New example hook post-receive.cgit-cache that clears cgit's output cache after
  a push, so new commits appear immediately instead of after the cache TTL.
* New CGIT_EXTRA_CFLAGS make variable applying extra compiler flags to cgit's
  own objects only, not the bundled git.

Changed
.......

* The tree was reorganized. The git submodule moved to vendor/git, and the
  example cgitrc, Lua filters, hooks and server configs moved into custom/.
  Operators following old paths must update them.
* Repositories with no commits get a dedicated page with working clone URLs
  instead of a bare notice with dead tabs.
* A description file still holding git's "Unnamed repository" boilerplate is
  treated as no description.
* The theme is applied before first paint, so pages no longer flash the wrong
  theme, and the header no longer shifts as the page loads.
* The nginx, Apache and lighttpd configs are complete, runnable files carrying
  their own top-level directives, rather than snippets assuming a distro base.
* The agefile hook uses committer dates from branches only, writes atomically
  and keeps its output readable by the web server. The update-server-info hook
  likewise forces a safe umask.
* The auth filters' secret moved from /var/cache/cgit to /var/lib/cgit, so
  pruning the cache no longer invalidates every live session.
* The dev preview server forwards cookies, referers and request bodies, so the
  auth filters can be exercised locally, and decodes escaped repository names.

Removed
.......

* The built-in help page and its enable-help option.
* GitHub Actions CI, the release workflow and the make dist target.

Fixed
.....

* Ordinary working trees found by scan-path are listed as repo instead of
  repo/.git.
* The auth filters match cookie names containing pattern magic characters, and
  auth-file tolerates a users file saved with CRLF line endings.


v2.2.0 (2026-07-25)
-------------------

A hardening release that puts explicit ceilings on the work a single request can
provoke, reworks the statistics page, and overhauls the bundled Lua filters.

Added
.....

* The stats page gains a language breakdown, sizing the tree at HEAD by file
  extension without ever loading blob contents.
* New enable-stats option, default 0, as the dedicated switch for the statistics
  page.
* New max-patch-count option, default 50, bounding how many commits the patch
  view emits as a series.
* New about-filter script about-render.lua, a server-side renderer for markdown,
  man pages and plain text, falling back to escaped text when its dependencies
  are missing.
* New make dist target staging the release tarball, so local and released
  tarballs are identical.

Changed
.......

* max-stats no longer enables the stats page, it only bounds the coarsest
  period. Instances relying on it must now also set enable-stats=1.
* The auth filters are hardened. Cookies are Secure by default, redirect targets
  are validated so a login cannot bounce to another origin, password checks are
  constant time even for unknown users, and a protected repository with no
  resolvable users denies everyone.
* The avatar filters skip missing addresses instead of hashing garbage,
  normalize the rest, and expose size, style and URL settings.
* link-commits.lua takes a user-editable list of pattern and URL rules instead
  of a hardcoded issue reference, resolving all matches in one pass.
* The syntax highlighter falls back through an extension map when lexer
  detection fails and emits its plain-text fallback in slices instead of one
  full-size copy.
* The masthead reserves the logo column so the header no longer shifts while the
  logo loads, and the logo and favicon were redrawn at higher resolution.

Removed
.......

* The client-side markdown renderer and the enable-markdown option. Rendered
  readmes now require about-filter pointed at about-render.lua.
* The PDF documentation targets.

Fixed
.....

* A file name containing a quote could break out of the link attributes in
  side-by-side diffs. Paths are now percent-encoded.
* A commit with no message no longer crashes the history views.
* max-blob-size also bounds the diff path, so a huge blob is reported as binary
  instead of inflated into memory, and dangling refs are skipped instead of
  dereferenced.
* The diff size caps are no longer silently disabled when follow is active.
* The header branch switcher respects max-ref-count instead of emitting an
  option per branch on every page.
* zstd snapshots run single-threaded, so one request cannot fan out across every
  core.
* The stats walk is pruned by date instead of visiting all history, and a commit
  whose date cannot be represented is dropped rather than indexing a month table
  out of bounds.
* The cache listing bounds its key output, and a malformed cgitrc line no longer
  discards the rest of the file.
* The build no longer triggers a section-alignment warning on every macOS link.


v2.1.0 (2026-07-19)
-------------------

A release about behaving well on large repositories and under a strict
Content-Security-Policy. Syntax highlighting moves out of the browser and into
an optional server-side filter.

Added
.....

* New max-ref-count option, default 200, capping how many branches and tags each
  refs section lists, with independent pagination on the dedicated branch and
  tag pages.
* New max-diff-files option, default 200. A commit touching more files renders
  only its diffstat, with a notice pointing at the per-file diffs and the patch
  view.
* New max-diff-lines option, default 1000. A single file exceeding it within a
  whole-commit view links to its own diff page instead of rendering inline.
  Single-file diffs, rawdiff and patch are never capped.
* An optional server-side syntax highlighter, syntax-highlight.lua, built on the
  Scintillua lexers with around 120 languages, degrading to plain escaped text
  when its dependencies are absent.
* A built-in help page documenting the site's URL patterns, behind the new
  enable-help option. Removed again in v2.3.0.
* URL fragments highlight the targeted source line in blob and blame views,
  support ranges like #n5-n12, and land the target mid-viewport.

Changed
.......

* The auto-submitting select controls drop their inline onchange handlers and
  are wired up from cgit.js, so the option forms work under a CSP without
  unsafe-inline.
* A plaintext readme keeps its line structure instead of collapsing into a
  run-on paragraph.
* Syntax highlighting of source views moved from the browser to the optional
  filter. Without a source-filter, code is served plain, and the client-side
  highlighter is deleted.
* Font sizes were evened out across the interface, and the external-link icon
  follows the tab's text color in both themes.
* The mobile layout hides the author and size columns, the search form and the
  branch switcher, so nothing forces sideways scrolling.

Removed
.......

* The owner-filter hook and its per-repository override. Owners always render as
  plain linked text.

Fixed
.....

* Release binaries no longer ship with an empty version string when git describe
  fails in a shallow clone.


v2.0.0 (2026-07-16)
-------------------

First release of this fork, cut from upstream cgit v1.3.1. The page chrome is
rebuilt as semantic HTML with dark mode and a phone layout, the runtime sheds
its external interpreters and crypto libraries, and around two dozen security
and correctness fixes land.

Added
.....

* Built-in client-side syntax highlighting in cgit.js for roughly 33 languages,
  used when no source-filter is configured.
* Built-in client-side markdown rendering for about pages behind the new
  enable-markdown option, restricted to safe link and image targets.
* A light and dark theme with a toggle cycling auto, light and dark, persisted
  in localStorage and invisible without JavaScript.
* A responsive layout for phones and small screens, with stacking panels and a
  repository index that collapses to name and age.
* New enable-tree-group-dirs option listing directories before files in the tree
  view.
* New enable-cache-list option, default 0, gating the previously unprotected
  cache listing page.
* A fully commented example cgitrc listing every option at its default, and
  complete nginx, Apache and lighttpd examples with security headers.
* An example post-update hook running update-server-info, needed because the
  built-in clone support speaks dumb HTTP.
* tools/serve.py, a dependency-free local preview server, and
  tools/release-build.sh, a hardened Linux release build.
* CI covering gcc and clang, the test suite under ASan and UBSan, sparse, and a
  hardened PIE build, plus a tag-triggered release workflow shipping hardened
  tarballs with checksums.
* Accessible names on the search field, branch switcher and breadcrumb
  navigation, and contextual titles on the nav tabs.

Changed
.......

* max-blob-size defaults to 10 MB instead of unlimited, and now caps everything
  cgit reads into memory to serve, including plain and blob output. Oversized
  objects return a 413 page.
* section-sort defaults to 0, so the order repositories are written in cgitrc is
  respected rather than alphabetized.
* Repository age on the index is derived from HEAD instead of a hardcoded
  refs/heads/master, so repositories on main show an age.
* The page chrome is semantic HTML instead of nested tables, keeping the
  existing class and id names so custom themes still match.
* The filter scripts moved to a Lua-only set. simple-authentication.lua became
  auth-inline.lua, file-authentication.lua became auth-file.lua and
  commit-links.sh became link-commits.lua.
* Lua is optional and autodetected through pkg-config, preferring LuaJIT.
  NO_LUA=1 forces a self-contained binary.
* The sources were reorganized into source/, libraries/, assets/, extensions/,
  examples/, tools/ and tests/, with all generated output under build/.
* The auth filters compare cookie HMACs in constant time, set SameSite=Lax, and
  strip header injection from Set-Cookie and Location values.

Removed
.......

* OpenSSL is no longer a build dependency, and libcurl is not required.
* Every Python filter script, including the Pygments highlighter, the markdown
  and rst converters and the Python gravatar filter.
* The Gentoo LDAP auth filter, owner-example.lua, about-formatting.sh and the
  man and txt about converters.
* The unused name query parameter and the never-read cache-max-create-time and
  max-lock-attempts settings.

Fixed
.....

* An unauthenticated arbitrary file write through the log id parameter, which
  reached git's revision parser as an option. Revisions beginning with a dash
  are rejected.
* Cache poisoning through a spoofed Host header. The scheme and host are now
  part of the cache key.
* Stored XSS from about pages, which were written as raw HTML when no
  about-filter was configured. They are now escaped.
* Two path traversals, one past the about-path prefix check via a sibling
  directory sharing a name prefix, and one through a crafted HEAD ref.
* max-blob-size is enforced before a blob is read into memory, not after.
* A signed-comparison bypass of the authentication POST size limit, and an
  unbounded history walk from a crafted ofs value, now clamped.
* Every snapshot was undecompressable whenever a global git config existed,
  because git's error output was compressed into the archive.
* Numerous crashes, among them out-of-bounds accesses on short paths and empty
  URLs, NULL dereferences on odd blobs and authorless commits, a division by
  zero in the diffstat and undefined ctype behavior on negative chars.
* Truncated pages after stat-only diffs and binary blames, a 200 instead of a
  404 for unknown blob paths, submodule hashes losing digits in diffs, pager
  links dropping search terms containing reserved characters, a missing updated
  element in empty atom feeds and negative ages from the age refresher.