aboutsummaryrefslogtreecommitdiff
diff refs
from:
to:
flip
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--AUTHORS12
-rw-r--r--Makefile6
-rw-r--r--README99
-rw-r--r--README.md95
-rw-r--r--cgit.c12
-rw-r--r--cgit.css407
-rw-r--r--cgit.h2
-rw-r--r--cgit.svg5
-rw-r--r--cgitrc.5.txt19
-rw-r--r--cmd.c4
-rw-r--r--favicon.svg5
-rw-r--r--filter.c4
-rwxr-xr-xfilters/html-converters/md2html84
-rwxr-xr-xfilters/syntax-highlighting.py45
-rwxr-xr-xfilters/syntax-highlighting.sh14
-rw-r--r--shared.c12
-rw-r--r--solarized.css140
-rwxr-xr-xtests/t0106-diff.sh61
-rw-r--r--ui-ci.c2
-rw-r--r--ui-diff.c78
-rw-r--r--ui-shared.c87
22 files changed, 895 insertions, 299 deletions
diff --git a/.gitignore b/.gitignore
index 661df34..084a985 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ cgitrc.5.pdf
cgitrc.5.xml
*.o
*.d
+__pycache__/
diff --git a/AUTHORS b/AUTHORS
index 031de33..e68846e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,13 +1,5 @@
Maintainer:
- Jason A. Donenfeld <Jason@zx2c4.com>
+ Saya Andy <saya.andy@posteo.com>
Contributors:
- Jason A. Donenfeld <Jason@zx2c4.com>
- Lukas Fleischer <cgit@cryptocrack.de>
- Johan Herland <johan@herland.net>
- Lars Hjemli <hjemli@gmail.com>
- Ferry Huberts <ferry.huberts@pelagic.nl>
- John Keeping <john@keeping.me.uk>
-
-Previous Maintainer:
- Lars Hjemli <hjemli@gmail.com>
+ Saya Andy <saya.andy@posteo.com>
diff --git a/Makefile b/Makefile
index 4e851af..b87116a 100644
--- a/Makefile
+++ b/Makefile
@@ -87,9 +87,12 @@ install: all
$(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
+ $(INSTALL) -m 0644 solarized.css $(DESTDIR)$(CGIT_DATA_PATH)/solarized.css
$(INSTALL) -m 0644 cgit.js $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js
$(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
+ $(INSTALL) -m 0644 cgit.svg $(DESTDIR)$(CGIT_DATA_PATH)/cgit.svg
$(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
+ $(INSTALL) -m 0644 favicon.svg $(DESTDIR)$(CGIT_DATA_PATH)/favicon.svg
$(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
$(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
$(COPYTREE) filters/* $(DESTDIR)$(filterdir)
@@ -116,9 +119,12 @@ endef
uninstall:
rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
+ rm -f $(DESTDIR)$(CGIT_DATA_PATH)/solarized.css
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
+ rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.svg
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
+ rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.svg
rm -f $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
$(foreach file,$(patsubst filters/%,%,$(shell find filters/ ! -type d)), \
$(call rm_f,$(DESTDIR)$(filterdir)/$(file)))
diff --git a/README b/README
deleted file mode 100644
index 7a6b4a4..0000000
--- a/README
+++ /dev/null
@@ -1,99 +0,0 @@
-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.
-
-Installation
-------------
-
-Building cgit involves building a proper version of Git. How to do this
-depends on how you obtained the cgit sources:
-
-a) If you're working in a cloned cgit repository, you first need to
-initialize and update the Git submodule:
-
- $ git submodule init # register the Git submodule in .git/config
- $ $EDITOR .git/config # if you want to specify a different url for git
- $ git submodule update # clone/fetch and checkout correct git version
-
-b) If you're building from a cgit tarball, you can download a proper git
-version like this:
-
- $ make get-git
-
-When either a) or b) has been performed, you can build and install cgit like
-this:
-
- $ make
- $ sudo make install
-
-This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You
-can configure this location (and a few other things) by providing a `cgit.conf`
-file (see the Makefile for details).
-
-If you'd like to compile without Lua support, you may use:
-
- $ make NO_LUA=1
-
-And if you'd like to specify a Lua implementation, you may use:
-
- $ make LUA_PKGCONFIG=lua5.1
-
-If this is not specified, the Lua implementation will be auto-detected,
-preferring LuaJIT if many are present. Acceptable values are generally "lua",
-"luajit", "lua5.1", and "lua5.2".
-
-
-Dependencies
-------------
-
-* libzip
-* libcrypto (OpenSSL)
-* libssl (OpenSSL)
-* optional: luajit or lua, most reliably used when pkg-config is available
-
-Apache configuration
---------------------
-
-A new `Directory` section must probably be added for cgit, possibly something
-like this:
-
- <Directory "/var/www/htdocs/cgit/">
- AllowOverride None
- Options +ExecCGI
- Order allow,deny
- Allow from all
- </Directory>
-
-
-Runtime configuration
----------------------
-
-The file `/etc/cgitrc` is read by cgit before handling a request. In addition
-to runtime parameters, this file may also contain a list of repositories
-displayed by cgit (see `cgitrc.5.txt` for further details).
-
-The cache
----------
-
-When cgit is invoked it looks for a cache file matching the request and
-returns it to the client. If no such cache file exists (or if it has expired),
-the content for the request is written into the proper cache file before the
-file is returned.
-
-If the cache file has expired but cgit is unable to obtain a lock for it, the
-stale cache file is returned to the client. This is done to favour page
-throughput over page freshness.
-
-The generated content contains the complete response to the client, including
-the HTTP headers `Modified` and `Expires`.
-
-Online presence
----------------
-
-* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/>
-
-* Patches, bug reports, discussions and support should go to the cgit
- mailing list: <cgit@lists.zx2c4.com>. To sign up, visit
- <https://lists.zx2c4.com/mailman/listinfo/cgit>
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1e13a37
--- /dev/null
+++ b/README.md
@@ -0,0 +1,95 @@
+## cgitext – CGI for Git extension by Saya Andy
+
+This is a custom expansion to cgit. Its most prominent changes by now are:
+
+* CI/CD tab, linking to an external service on ref pages by using template. The Jenkins plugin is added to support a scenario when a ref lacks a pipeline.
+
+* Theming now relies on CSS variables set in cgit.css, to allow easier custom additions and implicit filter theming.
+
+* Allow to use multiple favicons and of MIME type different from x-icon (f.ex. svg/png/webp).
+
+* Use vector-based format for favicon and cgit logo and make it default.
+
+* Include optional solarized.css addon theme.
+
+* Allow user to pick variable refs for diff in web ui.
+
+The original cgit project is hosted at [git.zx2c4.com](https://git.zx2c4.com/cgit/about/)
+
+## 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.
+
+### Installation
+
+Building cgit involves building a proper version of Git. How to do this depends on how you obtained the cgit sources:
+
+1. If you're working in a cloned cgit repository, you first need to initialize and update the Git submodule:
+
+ $ git submodule init # register the Git submodule in .git/config
+ $ $EDITOR .git/config # if you want to specify a different url for git
+ $ git submodule update # clone/fetch and checkout correct git version
+
+2. If you're building from a cgit tarball, you can download a proper git version like this:
+
+ $ make get-git
+
+When either `1.` or `2.` has been performed, you can build and install cgit like
+this:
+```
+make
+sudo make install
+```
+
+This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You can configure this location (and a few other things) by providing a `cgit.conf` file (see the Makefile for details).
+
+If you'd like to compile without Lua support, you may use:
+```
+make NO_LUA=1
+```
+
+And if you'd like to specify a Lua implementation, you may use:
+```
+make LUA_PKGCONFIG=lua5.1
+```
+
+If this is not specified, the Lua implementation will be auto-detected, preferring LuaJIT if many are present. Acceptable values are generally "lua", "luajit", "lua5.1", and "lua5.2".
+
+
+### Dependencies
+
+* libzip
+* libcrypto (OpenSSL)
+* libssl (OpenSSL)
+* optional: luajit or lua, most reliably used when pkg-config is available
+
+### Apache configuration
+
+A new `Directory` section must probably be added for cgit, possibly something like this:
+
+```
+<Directory "/var/www/htdocs/cgit/">
+ AllowOverride None
+ Options +ExecCGI
+ Order allow,deny
+ Allow from all
+</Directory>
+```
+
+### Runtime configuration
+
+The file `/etc/cgitrc` is read by cgit before handling a request. In addition to runtime parameters, this file may also contain a list of repositories displayed by cgit (see `cgitrc.5.txt` for further details).
+
+### The cache
+
+When cgit is invoked it looks for a cache file matching the request and returns it to the client. If no such cache file exists (or if it has expired), the content for the request is written into the proper cache file before the file is returned.
+
+If the cache file has expired but cgit is unable to obtain a lock for it, the stale cache file is returned to the client. This is done to favour page throughput over page freshness.
+
+The generated content contains the complete response to the client, including the HTTP headers `Modified` and `Expires`.
+
+### Online presence
+
+* The cgit homepage is hosted by cgit at [git.zx2c4.com](https://git.zx2c4.com/cgit/about/)
+
+* Patches, bug reports, discussions and support should go to the cgit mailing list: [cgit@lists.zx2c4.com](mailto:cgit@lists.zx2c4.com). To sign up, visit [lists.zx2c4.com](https://lists.zx2c4.com/mailman/listinfo/cgit)
diff --git a/cgit.c b/cgit.c
index 97a4180..bb32dc7 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,6 +1,11 @@
/* cgit.c: cgi for the git scm
*
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com>
+ *
+ * Modified 2026 by Saya Andy:
+ * Always check there is git repo on repo.path
+ * Allow to include multiple favicons
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -166,7 +171,7 @@ static void config_cb(const char *name, const char *value)
else if (!strcmp(name, "js"))
string_list_append(&ctx.cfg.js, strdup_first_line(value));
else if (!strcmp(name, "favicon"))
- ctx.cfg.favicon = strdup_first_line(value);
+ string_list_append(&ctx.cfg.favicon, strdup_first_line(value));
else if (!strcmp(name, "footer"))
ctx.cfg.footer = strdup_first_line(value);
else if (!strcmp(name, "head-include"))
@@ -408,8 +413,7 @@ static void prepare_context(void)
ctx.cfg.case_sensitive_sort = 1;
ctx.cfg.branch_sort = 0;
ctx.cfg.commit_sort = 0;
- ctx.cfg.logo = "/cgit.png";
- ctx.cfg.favicon = "/favicon.ico";
+ ctx.cfg.logo = "/cgit.svg";
ctx.cfg.local_time = 0;
ctx.cfg.enable_http_clone = 1;
ctx.cfg.enable_index_owner = 1;
@@ -997,7 +1001,7 @@ static void cgit_parse_args(int argc, const char **argv)
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--version")) {
- printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION);
+ printf("CGit %s | https://sayag.it/cgitext.git | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION);
#ifdef NO_LUA
printf("[-] ");
#else
diff --git a/cgit.css b/cgit.css
index d10a24d..7892eca 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,15 +1,88 @@
div#cgit {
+ --bg: white;
+ --bg-zebra: #f7f7f7;
+ --bg-hl: #eee;
+ --bg-hl2: #ccc;
+ --fg: #333;
+ --fg-strong: #000;
+ --fg-mid: #444;
+ --fg-mute: #777;
+ --fg-dim: #888;
+ --fg-faint: #bbb;
+ --fg-faintest: #ccc;
+ --fg-on-tint: black;
+ --fg-linenum: gray;
+ --border: #ccc;
+ --border-mid: #aaa;
+ --border-dark: #777;
+ --border-strong: black;
+
+ --red: red;
+ --red-mid: #c00;
+ --red-dark: #800;
+ --green: green;
+ --green-mid: #080;
+ --green-dark: #040;
+ --blue: blue;
+ --blue-dark: #009;
+
+ --graph-col1: #a00;
+ --graph-col2: #0a0;
+ --graph-col3: #aa0;
+ --graph-col4: #00a;
+ --graph-col5: #a0a;
+ --graph-col6: #0aa;
+
+ --graph-add-bg: #5c5;
+ --graph-rem-bg: #c55;
+
+ --notes-bg: #ffd;
+ --notes-border: #ee9;
+ --add-bg: #cfc;
+ --add-bg-dark: #aca;
+ --del-bg: #fcc;
+ --del-bg-dark: #caa;
+ --changed-bg: #ffc;
+ --changed-bg-dark: #cca;
+ --hunk-bg: #ccf;
+
+ --deco-branch-bg: #8f8;
+ --deco-branch-border: #070;
+ --deco-tag-bg: #ff8;
+ --deco-tag-border: #770;
+ --deco-tag-annotated-bg: #fc8;
+ --deco-tag-annotated-border: #770;
+ --deco-remote-bg: #ccf;
+ --deco-remote-border: #007;
+ --deco-other-bg: #f88;
+ --deco-other-border: #700;
+
+ --syn-keyword: #000;
+ --syn-keyword-alt: #0057ae;
+ --syn-type: #010181;
+ --syn-string: #bf0303;
+ --syn-string-alt: #818100;
+ --syn-escape: #f0f;
+ --syn-comment: #838183;
+ --syn-number: #b07e00;
+ --syn-preproc: #008200;
+ --syn-operator: #000;
+ --syn-interpolation: #0057ae;
+ --syn-linenum: #555;
+ --syn-error: #bf0303;
+ --syn-mark-bg: #ffb;
+
padding: 0em;
margin: 0em;
font-family: sans-serif;
font-size: 10pt;
- color: #333;
- background: white;
+ color: var(--fg);
+ background: var(--bg);
padding: 4px;
}
div#cgit a {
- color: blue;
+ color: var(--blue);
text-decoration: none;
}
@@ -38,7 +111,7 @@ div#cgit table#header td.main {
}
div#cgit table#header td.main a {
- color: #000;
+ color: var(--fg-strong);
}
div#cgit table#header td.form {
@@ -56,13 +129,13 @@ div#cgit table#header td.form select {
}
div#cgit table#header td.sub {
- color: #777;
- border-top: solid 1px #ccc;
+ color: var(--fg-mute);
+ border-top: solid 1px var(--border);
padding-left: 10px;
}
div#cgit table.tabs {
- border-bottom: solid 3px #ccc;
+ border-bottom: solid 3px var(--border);
border-collapse: collapse;
margin-top: 2em;
margin-bottom: 0px;
@@ -76,13 +149,13 @@ div#cgit table.tabs td {
div#cgit table.tabs td a {
padding: 2px 0.75em;
- color: #777;
+ color: var(--fg-mute);
font-size: 110%;
}
div#cgit table.tabs td a.active {
- color: #000;
- background-color: #ccc;
+ color: var(--fg-strong);
+ background-color: var(--bg-hl2);
}
div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after {
@@ -109,14 +182,14 @@ div#cgit table.tabs td.form select {
div#cgit div.path {
margin: 0px;
padding: 5px 2em 2px 2em;
- color: #000;
- background-color: #eee;
+ color: var(--fg-strong);
+ background-color: var(--bg-hl);
}
div#cgit div.content {
margin: 0px;
padding: 2em;
- border-bottom: solid 3px #ccc;
+ border-bottom: solid 3px var(--border);
}
@@ -127,46 +200,46 @@ div#cgit table.list {
}
div#cgit table.list tr {
- background: white;
+ background: var(--bg);
}
div#cgit table.list tr.logheader {
- background: #eee;
+ background: var(--bg-hl);
}
div#cgit table.list tr:nth-child(even) {
- background: #f7f7f7;
+ background: var(--bg-zebra);
}
div#cgit table.list tr:nth-child(odd) {
- background: white;
+ background: var(--bg);
}
div#cgit table.list tr:hover {
- background: #eee;
+ background: var(--bg-hl);
}
div#cgit table.list tr.nohover {
- background: white;
+ background: var(--bg);
}
div#cgit table.list tr.nohover:hover {
- background: white;
+ background: var(--bg);
}
div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
- background: #f7f7f7;
+ background: var(--bg-zebra);
}
div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) {
- background: white;
+ background: var(--bg);
}
div#cgit table.list th {
font-weight: bold;
- /* color: #888;
- border-top: dashed 1px #888;
- border-bottom: dashed 1px #888;
+ /* color: var(--fg-dim);
+ border-top: dashed 1px var(--fg-dim);
+ border-bottom: dashed 1px var(--fg-dim);
*/
padding: 0.1em 0.5em 0.05em 0.5em;
vertical-align: baseline;
@@ -183,27 +256,27 @@ div#cgit table.list td.commitgraph {
}
div#cgit table.list td.commitgraph .column1 {
- color: #a00;
+ color: var(--graph-col1);
}
div#cgit table.list td.commitgraph .column2 {
- color: #0a0;
+ color: var(--graph-col2);
}
div#cgit table.list td.commitgraph .column3 {
- color: #aa0;
+ color: var(--graph-col3);
}
div#cgit table.list td.commitgraph .column4 {
- color: #00a;
+ color: var(--graph-col4);
}
div#cgit table.list td.commitgraph .column5 {
- color: #a0a;
+ color: var(--graph-col5);
}
div#cgit table.list td.commitgraph .column6 {
- color: #0aa;
+ color: var(--graph-col6);
}
div#cgit table.list td.logsubject {
@@ -218,16 +291,16 @@ div#cgit table.list td.logmsg {
}
div#cgit table.list td a {
- color: black;
+ color: var(--fg-strong);
}
div#cgit table.list td a.ls-dir {
font-weight: bold;
- color: #00f;
+ color: var(--blue);
}
div#cgit table.list td a:hover {
- color: #00f;
+ color: var(--blue);
}
div#cgit img {
@@ -261,21 +334,21 @@ div#cgit div#summary {
div#cgit table#downloads {
float: right;
border-collapse: collapse;
- border: solid 1px #777;
+ border: solid 1px var(--border-dark);
margin-left: 0.5em;
margin-bottom: 0.5em;
}
div#cgit table#downloads th {
- background-color: #ccc;
+ background-color: var(--bg-hl2);
}
div#cgit div#blob {
- border: solid 1px black;
+ border: solid 1px var(--border-strong);
}
div#cgit div.error {
- color: red;
+ color: var(--red);
font-weight: bold;
margin: 1em 2em;
}
@@ -297,21 +370,21 @@ div#cgit td.ls-mode {
div#cgit table.blob {
margin-top: 0.5em;
- border-top: solid 1px black;
+ border-top: solid 1px var(--border-strong);
}
div#cgit table.blob td.hashes,
div#cgit table.blob td.lines {
margin: 0; padding: 0 0 0 0.5em;
vertical-align: top;
- color: black;
+ color: var(--fg-strong);
}
div#cgit table.blob td.linenumbers {
margin: 0; padding: 0 0.5em 0 0.5em;
vertical-align: top;
text-align: right;
- border-right: 1px solid gray;
+ border-right: 1px solid var(--fg-linenum);
}
div#cgit table.blob pre {
@@ -320,18 +393,18 @@ div#cgit table.blob pre {
div#cgit table.blob td.linenumbers a,
div#cgit table.ssdiff td.lineno a {
- color: gray;
+ color: var(--fg-linenum);
text-align: right;
text-decoration: none;
}
div#cgit table.blob td.linenumbers a:hover,
div#cgit table.ssdiff td.lineno a:hover {
- color: black;
+ color: var(--fg-strong);
}
div#cgit table.blob td.linenumbers a:target:before {
- color: red;
+ color: var(--red);
content: "\2BA9";
}
@@ -351,11 +424,11 @@ div#cgit table.blame td.linenumbers div.alt {
}
div#cgit table.blame div.alt:nth-child(even) {
- background: #eee;
+ background: var(--bg-hl);
}
div#cgit table.blame div.alt:nth-child(odd) {
- background: white;
+ background: var(--bg);
}
div#cgit table.blame td.lines > div {
@@ -374,20 +447,20 @@ div#cgit table.blame .oid {
div#cgit table.bin-blob {
margin-top: 0.5em;
- border: solid 1px black;
+ border: solid 1px var(--border-strong);
}
div#cgit table.bin-blob th {
font-family: monospace;
white-space: pre;
- border: solid 1px #777;
+ border: solid 1px var(--border-dark);
padding: 0.5em 1em;
}
div#cgit table.bin-blob td {
font-family: monospace;
white-space: pre;
- border-left: solid 1px #777;
+ border-left: solid 1px var(--border-dark);
padding: 0em 1em;
}
@@ -405,10 +478,18 @@ div#cgit div.cgit-panel {
margin-top: 1.5em;
}
+div#cgit form.diff-ctrls div.cgit-panel {
+ float: none;
+}
+
+div#cgit form.diff-ctrls {
+ float: right;
+}
+
div#cgit div.cgit-panel table {
border-collapse: collapse;
- border: solid 1px #aaa;
- background-color: #eee;
+ border: solid 1px var(--border-mid);
+ background-color: var(--bg-hl);
}
div#cgit div.cgit-panel th {
@@ -459,8 +540,8 @@ div#cgit div.notes-header {
div#cgit div.notes {
white-space: pre;
font-family: monospace;
- border: solid 1px #ee9;
- background-color: #ffd;
+ border: solid 1px var(--notes-border);
+ background-color: var(--notes-bg);
padding: 0.3em 2em 0.3em 1em;
float: left;
}
@@ -476,8 +557,8 @@ div#cgit div.diffstat-header {
div#cgit table.diffstat {
border-collapse: collapse;
- border: solid 1px #aaa;
- background-color: #eee;
+ border: solid 1px var(--border-mid);
+ background-color: var(--bg-hl);
}
div#cgit table.diffstat th {
@@ -500,19 +581,19 @@ div#cgit table.diffstat td.mode {
div#cgit table.diffstat td span.modechange {
padding-left: 1em;
- color: red;
+ color: var(--red);
}
div#cgit table.diffstat td.add a {
- color: green;
+ color: var(--green);
}
div#cgit table.diffstat td.del a {
- color: red;
+ color: var(--red);
}
div#cgit table.diffstat td.upd a {
- color: blue;
+ color: var(--blue);
}
div#cgit table.diffstat td.graph {
@@ -531,15 +612,15 @@ div#cgit table.diffstat td.graph td {
}
div#cgit table.diffstat td.graph td.add {
- background-color: #5c5;
+ background-color: var(--graph-add-bg);
}
div#cgit table.diffstat td.graph td.rem {
- background-color: #c55;
+ background-color: var(--graph-rem-bg);
}
div#cgit div.diffstat-summary {
- color: #888;
+ color: var(--fg-dim);
padding-top: 0.5em;
}
@@ -555,19 +636,19 @@ div#cgit table.diff td {
div#cgit table.diff td div.head {
font-weight: bold;
margin-top: 1em;
- color: black;
+ color: var(--fg-strong);
}
div#cgit table.diff td div.hunk {
- color: #009;
+ color: var(--blue-dark);
}
div#cgit table.diff td div.add {
- color: green;
+ color: var(--green);
}
div#cgit table.diff td div.del {
- color: red;
+ color: var(--red);
}
div#cgit .oid {
@@ -585,7 +666,7 @@ div#cgit .right {
div#cgit table.list td.reposection {
font-style: italic;
- color: #888;
+ color: var(--fg-dim);
}
div#cgit a.button {
@@ -622,7 +703,7 @@ div#cgit ul.pager li {
}
div#cgit ul.pager a {
- color: #777;
+ color: var(--fg-mute);
}
div#cgit ul.pager .current {
@@ -631,46 +712,46 @@ div#cgit ul.pager .current {
div#cgit span.age-mins {
font-weight: bold;
- color: #080;
+ color: var(--green-mid);
}
div#cgit span.age-hours {
- color: #080;
+ color: var(--green-mid);
}
div#cgit span.age-days {
- color: #040;
+ color: var(--green-dark);
}
div#cgit span.age-weeks {
- color: #444;
+ color: var(--fg-mid);
}
div#cgit span.age-months {
- color: #888;
+ color: var(--fg-dim);
}
div#cgit span.age-years {
- color: #bbb;
+ color: var(--fg-faint);
}
div#cgit span.insertions {
- color: #080;
+ color: var(--green-mid);
}
div#cgit span.deletions {
- color: #800;
+ color: var(--red-dark);
}
div#cgit div.footer {
margin-top: 0.5em;
text-align: center;
font-size: 80%;
- color: #ccc;
+ color: var(--fg-faintest);
}
div#cgit div.footer a {
- color: #ccc;
+ color: var(--fg-faintest);
text-decoration: none;
}
@@ -679,47 +760,47 @@ div#cgit div.footer a:hover {
}
div#cgit a.branch-deco {
- color: #000;
+ color: var(--fg-strong);
margin: 0px 0.5em;
padding: 0px 0.25em;
- background-color: #88ff88;
- border: solid 1px #007700;
+ background-color: var(--deco-branch-bg);
+ border: solid 1px var(--deco-branch-border);
border-radius: 2px;
}
div#cgit a.tag-deco {
- color: #000;
+ color: var(--fg-strong);
margin: 0px 0.5em;
padding: 0px 0.25em;
- background-color: #ffff88;
- border: solid 1px #777700;
+ background-color: var(--deco-tag-bg);
+ border: solid 1px var(--deco-tag-border);
border-radius: 2px;
}
div#cgit a.tag-annotated-deco {
- color: #000;
+ color: var(--fg-strong);
margin: 0px 0.5em;
padding: 0px 0.25em;
- background-color: #ffcc88;
- border: solid 1px #777700;
+ background-color: var(--deco-tag-annotated-bg);
+ border: solid 1px var(--deco-tag-annotated-border);
border-radius: 2px;
}
div#cgit a.remote-deco {
- color: #000;
+ color: var(--fg-strong);
margin: 0px 0.5em;
padding: 0px 0.25em;
- background-color: #ccccff;
- border: solid 1px #000077;
+ background-color: var(--deco-remote-bg);
+ border: solid 1px var(--deco-remote-border);
border-radius: 2px;
}
div#cgit a.deco {
- color: #000;
+ color: var(--fg-strong);
margin: 0px 0.5em;
padding: 0px 0.25em;
- background-color: #ff8888;
- border: solid 1px #770000;
+ background-color: var(--deco-other-bg);
+ border: solid 1px var(--deco-other-border);
border-radius: 2px;
}
@@ -733,21 +814,21 @@ div#cgit div.commit-subject a.deco {
}
div#cgit table.stats {
- border: solid 1px black;
+ border: solid 1px var(--border-strong);
border-collapse: collapse;
}
div#cgit table.stats th {
text-align: left;
padding: 1px 0.5em;
- background-color: #eee;
- border: solid 1px black;
+ background-color: var(--bg-hl);
+ border: solid 1px var(--border-strong);
}
div#cgit table.stats td {
text-align: right;
padding: 1px 0.5em;
- border: solid 1px black;
+ border: solid 1px var(--border-strong);
}
div#cgit table.stats td.total {
@@ -756,9 +837,9 @@ div#cgit table.stats td.total {
}
div#cgit table.stats td.sum {
- color: #c00;
+ color: var(--red-mid);
font-weight: bold;
-/* background-color: #eee; */
+/* background-color: var(--bg-hl); */
}
div#cgit table.stats td.left {
@@ -767,14 +848,14 @@ div#cgit table.stats td.left {
div#cgit table.vgraph {
border-collapse: separate;
- border: solid 1px black;
+ border: solid 1px var(--border-strong);
height: 200px;
}
div#cgit table.vgraph th {
- background-color: #eee;
+ background-color: var(--bg-hl);
font-weight: bold;
- border: solid 1px white;
+ border: solid 1px var(--bg);
padding: 1px 0.5em;
}
@@ -784,18 +865,18 @@ div#cgit table.vgraph td {
}
div#cgit table.vgraph div.bar {
- background-color: #eee;
+ background-color: var(--bg-hl);
}
div#cgit table.hgraph {
- border: solid 1px black;
+ border: solid 1px var(--border-strong);
width: 800px;
}
div#cgit table.hgraph th {
- background-color: #eee;
+ background-color: var(--bg-hl);
font-weight: bold;
- border: solid 1px black;
+ border: solid 1px var(--border-strong);
padding: 1px 0.5em;
}
@@ -805,7 +886,7 @@ div#cgit table.hgraph td {
}
div#cgit table.hgraph div.bar {
- background-color: #eee;
+ background-color: var(--bg-hl);
height: 1em;
}
@@ -818,83 +899,83 @@ div#cgit table.ssdiff td {
font-family: monospace;
white-space: pre;
padding: 1px 4px 1px 4px;
- border-left: solid 1px #aaa;
- border-right: solid 1px #aaa;
+ border-left: solid 1px var(--border-mid);
+ border-right: solid 1px var(--border-mid);
}
div#cgit table.ssdiff td.add {
- color: black;
- background: #cfc;
+ color: var(--fg-on-tint);
+ background: var(--add-bg);
min-width: 50%;
}
div#cgit table.ssdiff td.add_dark {
- color: black;
- background: #aca;
+ color: var(--fg-on-tint);
+ background: var(--add-bg-dark);
min-width: 50%;
}
div#cgit table.ssdiff span.add {
- background: #cfc;
+ background: var(--add-bg);
font-weight: bold;
}
div#cgit table.ssdiff td.del {
- color: black;
- background: #fcc;
+ color: var(--fg-on-tint);
+ background: var(--del-bg);
min-width: 50%;
}
div#cgit table.ssdiff td.del_dark {
- color: black;
- background: #caa;
+ color: var(--fg-on-tint);
+ background: var(--del-bg-dark);
min-width: 50%;
}
div#cgit table.ssdiff span.del {
- background: #fcc;
+ background: var(--del-bg);
font-weight: bold;
}
div#cgit table.ssdiff td.changed {
- color: black;
- background: #ffc;
+ color: var(--fg-on-tint);
+ background: var(--changed-bg);
min-width: 50%;
}
div#cgit table.ssdiff td.changed_dark {
- color: black;
- background: #cca;
+ color: var(--fg-on-tint);
+ background: var(--changed-bg-dark);
min-width: 50%;
}
div#cgit table.ssdiff td.lineno {
- color: black;
- background: #eee;
+ color: var(--fg-on-tint);
+ background: var(--bg-hl);
text-align: right;
width: 3em;
min-width: 3em;
}
div#cgit table.ssdiff td.hunk {
- color: black;
- background: #ccf;
- border-top: solid 1px #aaa;
- border-bottom: solid 1px #aaa;
+ color: var(--fg-on-tint);
+ background: var(--hunk-bg);
+ border-top: solid 1px var(--border-mid);
+ border-bottom: solid 1px var(--border-mid);
}
div#cgit table.ssdiff td.head {
- border-top: solid 1px #aaa;
- border-bottom: solid 1px #aaa;
+ border-top: solid 1px var(--border-mid);
+ border-bottom: solid 1px var(--border-mid);
}
div#cgit table.ssdiff td.head div.head {
font-weight: bold;
- color: black;
+ color: var(--fg-on-tint);
}
div#cgit table.ssdiff td.foot {
- border-top: solid 1px #aaa;
+ border-top: solid 1px var(--border-mid);
border-left: none;
border-right: none;
border-bottom: none;
@@ -907,3 +988,63 @@ div#cgit table.ssdiff td.space {
div#cgit table.ssdiff td.space div {
min-height: 3em;
}
+
+div#cgit .hl.kwa,
+div#cgit .hl.kwc {
+ color: var(--syn-keyword);
+ font-weight: bold;
+}
+
+div#cgit .hl.kwb {
+ color: var(--syn-keyword-alt);
+}
+
+div#cgit .hl.kwd {
+ color: var(--syn-type);
+}
+
+div#cgit .hl.str {
+ color: var(--syn-string);
+}
+
+div#cgit .hl.dstr,
+div#cgit .hl.pps {
+ color: var(--syn-string-alt);
+}
+
+div#cgit .hl.esc {
+ color: var(--syn-escape);
+}
+
+div#cgit .hl.com,
+div#cgit .hl.slc {
+ color: var(--syn-comment);
+ font-style: italic;
+}
+
+div#cgit .hl.num {
+ color: var(--syn-number);
+}
+
+div#cgit .hl.dir,
+div#cgit .hl.ppc {
+ color: var(--syn-preproc);
+}
+
+div#cgit .hl.opt,
+div#cgit .hl.sym {
+ color: var(--syn-operator);
+}
+
+div#cgit .hl.ipl {
+ color: var(--syn-interpolation);
+}
+
+div#cgit .hl.lin,
+div#cgit .hl.line {
+ color: var(--syn-linenum);
+}
+
+div#cgit .hl.mark {
+ background-color: var(--syn-mark-bg);
+}
diff --git a/cgit.h b/cgit.h
index 847ab4e..e2b8cbc 100644
--- a/cgit.h
+++ b/cgit.h
@@ -205,7 +205,7 @@ struct cgit_config {
char *ci_tag_url;
char *clone_prefix;
char *clone_url;
- char *favicon;
+ struct string_list favicon;
char *footer;
char *head_include;
char *header;
diff --git a/cgit.svg b/cgit.svg
new file mode 100644
index 0000000..43fee2c
--- /dev/null
+++ b/cgit.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="96" height="64" viewBox="0 0 96 64" fill="none" stroke="#a00" stroke-width="5">
+ <title>cgit</title>
+ <path d="M31.5 27.5A9 9 0 0 0 22.5 18.5h-5A9 9 0 0 0 8.5 27.5V38a9 9 0 0 0 9 9h5a9 9 0 0 0 9-9M31.5 38v12.5a8 8 0 0 1-8 8H17a8 8 0 0 1-8-8M46.5 3v5m0 8v33M62.5 9v29.75a7.75 7.75 0 0 0 15.5 0V37"/>
+ <path stroke-width="4" d="M56 18h25"/>
+</svg>
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index c03579f..527233d 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -240,9 +240,14 @@ enable-tree-linenumbers::
for plaintext blobs printed in the tree view. Default value: "1".
favicon::
- Url used as link to a shortcut icon for cgit. It is suggested to use
- the value "/favicon.ico" since certain browsers will ignore other
- values. Default value: "/favicon.ico".
+ Url used as link to a shortcut icon for cgit. It is suggested to
+ always include the value "/favicon.ico" since certain browsers will
+ ignore other values. May be given multiple times, each favicon URL
+ path is added in the head section of the document in turn, letting
+ the browser pick the icon it prefers. The mimetype of each icon is
+ deduced from the extension of its url ("ico", "png", "svg", "gif",
+ "jpg", "jpeg", "webp" and "avif" are recognized) and emitted as a
+ "type" attribute. Default value: "/favicon.svg", "/favicon.ico".
footer::
The content of the file specified with this option will be included
@@ -272,7 +277,7 @@ local-time::
logo::
Url which specifies the source of an image which will be used as a logo
- on all cgit pages. Default value: "/cgit.png".
+ on all cgit pages. Default value: "/cgit.svg".
logo-link::
Url loaded when clicking on the cgit logo image. If unspecified the
@@ -916,8 +921,10 @@ cache-size=1000
# Specify some default clone urls using macro expansion
clone-url=git://foo.org/$CGIT_REPO_URL git@foo.org:$CGIT_REPO_URL
+
# Specify the css url
css=/css/cgit.css
+css=/css/solarized.css
# Add a "ci" tab pointing at a Jenkins multibranch pipeline
@@ -957,7 +964,8 @@ enable-log-linecount=1
branch-sort=age
-# Add a cgit favicon
+# Add cgit favicons
+favicon=/favicon.svg
favicon=/favicon.ico
@@ -1117,3 +1125,4 @@ AUTHOR
------
Lars Hjemli <hjemli@gmail.com>
Jason A. Donenfeld <Jason@zx2c4.com>
+Saya Andy <saya.andy@posteo.com>
diff --git a/cmd.c b/cmd.c
index 4c249c8..0ccd677 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,6 +1,10 @@
/* cmd.c: the cgit command dispatcher
*
* Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com>
+ *
+ * Modified 2026 by Saya Andy:
+ * Include ci ui element
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
diff --git a/favicon.svg b/favicon.svg
new file mode 100644
index 0000000..6be3df4
--- /dev/null
+++ b/favicon.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" viewBox="0 0 52 52" fill="none" stroke-width="6">
+ <title>cgit</title>
+ <g stroke="#f33"><path id="g" d="M37.5 15A9 9 0 0 0 28.5 6h-5A9 9 0 0 0 14.5 15v10.5a9 9 0 0 0 9 9h5a9 9 0 0 0 9-9M37.5 25.5V38a8 8 0 0 1-8 8H23a8 8 0 0 1-8-8"/></g>
+ <use href="#g" stroke="#a00" stroke-width="4"/>
+</svg>
diff --git a/filter.c b/filter.c
index 3b452cb..7d722a3 100644
--- a/filter.c
+++ b/filter.c
@@ -1,6 +1,10 @@
/* filter.c: filter framework functions
*
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com>
+ *
+ * Modified 2026 by Saya Andy:
+ * Integrate ci filter
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
diff --git a/filters/html-converters/md2html b/filters/html-converters/md2html
index 59f43a8..4f6ad37 100755
--- a/filters/html-converters/md2html
+++ b/filters/html-converters/md2html
@@ -2,8 +2,46 @@
import markdown
import sys
import io
-from pygments.formatters import HtmlFormatter
from markdown.extensions.toc import TocExtension
+
+# Pygments' token classes, mapped onto the --syn-* custom properties that
+# cgit.css defines. The fallbacks are the palette those properties default to,
+# so that the output stays readable under a stylesheet without them.
+SYNTAX_COLORS = (
+ (('k', 'kc', 'kd', 'kn', 'kr'), '--syn-keyword', '#000',
+ 'font-weight: bold'),
+ (('kp',), '--syn-keyword', '#000', None),
+ (('kt',), '--syn-type', '#010181', None),
+ (('nc', 'nf', 'nn', 'ne'), '--syn-keyword-alt', '#0057ae', None),
+ (('s', 'sa', 'sb', 'sc', 'dl', 'sd', 's2', 'sh', 'sx', 'sr', 's1', 'ss'),
+ '--syn-string', '#bf0303', None),
+ (('se',), '--syn-escape', '#f0f', None),
+ (('si',), '--syn-interpolation', '#0057ae', None),
+ (('c', 'ch', 'cm', 'c1', 'cs'), '--syn-comment', '#838183',
+ 'font-style: italic'),
+ (('cp', 'cpf'), '--syn-preproc', '#008200', None),
+ (('m', 'mb', 'mf', 'mh', 'mi', 'mo', 'il'), '--syn-number', '#b07e00',
+ None),
+ (('o', 'ow', 'p'), '--syn-operator', '#000', None),
+ (('err',), '--syn-error', '#bf0303', None),
+ (('gd',), '--red', 'red', None),
+ (('gi',), '--green', 'green', None),
+ (('lineno', 'linenos'), '--syn-linenum', '#555', None),
+)
+
+
+def style_defs(prefix='.highlight'):
+ rules = []
+ for classes, prop, fallback, extra in SYNTAX_COLORS:
+ selector = ', '.join('%s .%s' % (prefix, c) for c in classes)
+ decls = 'color: var(%s, %s)' % (prop, fallback)
+ if extra:
+ decls += '; ' + extra
+ rules.append('%s { %s }' % (selector, decls))
+ rules.append('%s .hll { background-color: var(--syn-mark-bg, #ffb) }'
+ % prefix)
+ return '\n'.join(rules) + '\n'
+
sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stdout.write('''
@@ -20,7 +58,7 @@ sys.stdout.write('''
margin-bottom: 0 !important;
}
.markdown-body a.absent {
- color: #c00;
+ color: var(--red-mid, #c00);
}
.markdown-body a.anchor {
display: block;
@@ -42,7 +80,7 @@ sys.stdout.write('''
}
.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
display: none;
- color: #000;
+ color: var(--fg-strong, #000);
}
.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
text-decoration: none;
@@ -55,19 +93,19 @@ sys.stdout.write('''
display: inline-block;
}
div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink {
- color: black;
+ color: var(--fg-strong, #000);
}
.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
font-size: inherit;
}
.markdown-body h1 {
font-size: 28px;
- color: #000;
+ color: var(--fg-strong, #000);
}
.markdown-body h2 {
font-size: 24px;
- border-bottom: 1px solid #ccc;
- color: #000;
+ border-bottom: 1px solid var(--border, #ccc);
+ color: var(--fg-strong, #000);
}
.markdown-body h3 {
font-size: 18px;
@@ -79,14 +117,14 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
font-size: 14px;
}
.markdown-body h6 {
- color: #777;
+ color: var(--fg-mute, #777);
font-size: 14px;
}
.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
margin: 15px 0;
}
.markdown-body hr {
- border: 2px solid #ccc;
+ border: 2px solid var(--border, #ccc);
}
.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
margin-top: 0;
@@ -148,9 +186,9 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
margin-bottom: 0px;
}
.markdown-body blockquote {
- border-left: 4px solid #DDD;
+ border-left: 4px solid var(--border, #ddd);
padding: 0 15px;
- color: #777;
+ color: var(--fg-mute, #777);
}
.markdown-body blockquote>:first-child {
margin-top: 0px;
@@ -162,15 +200,15 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
font-weight: bold;
}
.markdown-body table th, .markdown-body table td {
- border: 1px solid #ccc;
+ border: 1px solid var(--border, #ccc);
padding: 6px 13px;
}
.markdown-body table tr {
- border-top: 1px solid #ccc;
- background-color: #fff;
+ border-top: 1px solid var(--border, #ccc);
+ background-color: var(--bg, #fff);
}
.markdown-body table tr:nth-child(2n) {
- background-color: #f8f8f8;
+ background-color: var(--bg-zebra, #f8f8f8);
}
.markdown-body img {
max-width: 100%;
@@ -182,7 +220,7 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
overflow: hidden;
}
.markdown-body span.frame>span {
- border: 1px solid #ddd;
+ border: 1px solid var(--border, #ddd);
display: block;
float: left;
overflow: hidden;
@@ -196,7 +234,7 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
}
.markdown-body span.frame span span {
clear: both;
- color: #333;
+ color: var(--fg, #333);
display: block;
padding: 5px 0 0;
}
@@ -254,8 +292,8 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
.markdown-body code, .markdown-body tt {
margin: 0 2px;
padding: 0px 5px;
- border: 1px solid #eaeaea;
- background-color: #f8f8f8;
+ border: 1px solid var(--border, #eaeaea);
+ background-color: var(--bg-zebra, #f8f8f8);
border-radius: 3px;
}
.markdown-body code {
@@ -269,8 +307,8 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
background: transparent;
}
.markdown-body .highlight pre, .markdown-body pre {
- background-color: #f8f8f8;
- border: 1px solid #ccc;
+ background-color: var(--bg-zebra, #f8f8f8);
+ border: 1px solid var(--border, #ccc);
font-size: 13px;
line-height: 19px;
overflow: auto;
@@ -284,9 +322,9 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#
border: none;
}
''')
-sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
+sys.stdout.write(style_defs('.highlight'))
sys.stdout.write('''
-</style>
+</style>
''')
sys.stdout.write("<div class='markdown-body'>")
sys.stdout.flush()
diff --git a/filters/syntax-highlighting.py b/filters/syntax-highlighting.py
index e912594..d757e2d 100755
--- a/filters/syntax-highlighting.py
+++ b/filters/syntax-highlighting.py
@@ -30,11 +30,50 @@ from pygments.lexers import guess_lexer_for_filename
from pygments.formatters import HtmlFormatter
+# Pygments' token classes, mapped onto the --syn-* custom properties that
+# cgit.css defines. The fallbacks are the palette those properties default to,
+# so that the output stays readable under a stylesheet without them.
+SYNTAX_COLORS = (
+ (('k', 'kc', 'kd', 'kn', 'kr'), '--syn-keyword', '#000',
+ 'font-weight: bold'),
+ (('kp',), '--syn-keyword', '#000', None),
+ (('kt',), '--syn-type', '#010181', None),
+ (('nc', 'nf', 'nn', 'ne'), '--syn-keyword-alt', '#0057ae', None),
+ (('s', 'sa', 'sb', 'sc', 'dl', 'sd', 's2', 'sh', 'sx', 'sr', 's1', 'ss'),
+ '--syn-string', '#bf0303', None),
+ (('se',), '--syn-escape', '#f0f', None),
+ (('si',), '--syn-interpolation', '#0057ae', None),
+ (('c', 'ch', 'cm', 'c1', 'cs'), '--syn-comment', '#838183',
+ 'font-style: italic'),
+ (('cp', 'cpf'), '--syn-preproc', '#008200', None),
+ (('m', 'mb', 'mf', 'mh', 'mi', 'mo', 'il'), '--syn-number', '#b07e00',
+ None),
+ (('o', 'ow', 'p'), '--syn-operator', '#000', None),
+ (('err',), '--syn-error', '#bf0303', None),
+ (('gd',), '--red', 'red', None),
+ (('gi',), '--green', 'green', None),
+ (('lineno', 'linenos'), '--syn-linenum', '#555', None),
+)
+
+
+def style_defs(prefix='.highlight'):
+ rules = []
+ for classes, prop, fallback, extra in SYNTAX_COLORS:
+ selector = ', '.join('%s .%s' % (prefix, c) for c in classes)
+ decls = 'color: var(%s, %s)' % (prop, fallback)
+ if extra:
+ decls += '; ' + extra
+ rules.append('%s { %s }' % (selector, decls))
+ rules.append('%s .hll { background-color: var(--syn-mark-bg, #ffb) }'
+ % prefix)
+ return '\n'.join(rules) + '\n'
+
+
sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
data = sys.stdin.read()
filename = sys.argv[1]
-formatter = HtmlFormatter(style='pastie', nobackground=True)
+formatter = HtmlFormatter(nobackground=True)
try:
lexer = guess_lexer_for_filename(filename, data)
@@ -48,8 +87,8 @@ except TypeError:
lexer = TextLexer()
# highlight! :-)
-# printout pygments' css definitions as well
+# printout the css definitions for pygments' token classes as well
sys.stdout.write('<style>')
-sys.stdout.write(formatter.get_style_defs('.highlight'))
+sys.stdout.write(style_defs('.highlight'))
sys.stdout.write('</style>')
sys.stdout.write(highlight(data, lexer, formatter, outfile=None))
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
index 840bc34..bb8d60c 100755
--- a/filters/syntax-highlighting.sh
+++ b/filters/syntax-highlighting.sh
@@ -8,8 +8,10 @@
# might have to use an external call to sed instead.
#
# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
-# highlighting, so you'll probably want something like the following included
-# in your css file:
+# highlighting. cgit.css defines these classes in terms of its --syn-* custom
+# properties, so nothing has to be added when using the default stylesheet.
+# The palette below, from highlight 3.13, is what those properties default to;
+# a custom stylesheet wanting the same colors can use it directly:
#
# Style definition file generated by highlight 2.4.8, http://www.andre-simon.de/
#
@@ -114,8 +116,12 @@ EXTENSION="${BASENAME##*.}"
# Version 2 can be found (for example) on EPEL 5, while version 3 can be
# found (for example) on EPEL 6.
#
+# Note that -I (--include-style) is not used: the stylesheet it embeds in the
+# output would override the .hl rules in cgit.css and with them any theming
+# done through the --syn-* custom properties.
+#
# This is for version 2
-exec highlight --force -f -I -X -S "$EXTENSION" 2>/dev/null
+exec highlight --force -f -X -S "$EXTENSION" 2>/dev/null
# This is for version 3
-#exec highlight --force -f -I -O xhtml -S "$EXTENSION" 2>/dev/null
+#exec highlight --force -f -O xhtml -S "$EXTENSION" 2>/dev/null
diff --git a/shared.c b/shared.c
index 83b5626..8beb7c7 100644
--- a/shared.c
+++ b/shared.c
@@ -1,6 +1,11 @@
/* shared.c: global vars + some callback functions
*
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com>
+ *
+ * Modified 2026 by Saya Andy:
+ * Add cgit_have_repository()
+ * Add ci filter configuration
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -13,13 +18,6 @@
struct cgit_repolist cgit_repolist;
struct cgit_context ctx;
-/* Returns true when the git repository belonging to this request was opened
- * successfully. Some requests print a page for a repository which could not
- * be opened, e.g. because repo.path is wrong, and the object database and
- * ref stores must not be touched in that case: they would abort with
- * "reference backend is unknown" or dereference the not yet known hash
- * algorithm.
- */
int cgit_have_repository(void)
{
return startup_info->have_repository;
diff --git a/solarized.css b/solarized.css
new file mode 100644
index 0000000..2e6e3d4
--- /dev/null
+++ b/solarized.css
@@ -0,0 +1,140 @@
+/*
+ * Solarized theme (Ethan Schoonover) with automatic light/dark selection
+ * based on the prefers-color-scheme media query.
+ *
+ * Must be listed after cgit.css, e.g.
+ *
+ * css=/cgit.css
+ * css=/solarized.css
+ */
+
+:root {
+ --sol-base03: #002b36;
+ --sol-base02: #073642;
+ --sol-base01: #586e75;
+ --sol-base00: #657b83;
+ --sol-base0: #839496;
+ --sol-base1: #93a1a1;
+ --sol-base2: #eee8d5;
+ --sol-base3: #fdf6e3;
+
+ --sol-yellow: #b58900;
+ --sol-orange: #cb4b16;
+ --sol-red: #dc322f;
+ --sol-magenta: #d33682;
+ --sol-violet: #6c71c4;
+ --sol-blue: #268bd2;
+ --sol-cyan: #2aa198;
+ --sol-green: #859900;
+
+ --sol-canvas: var(--sol-base3);
+ --sol-panel: var(--sol-base2);
+ --sol-body: var(--sol-base00);
+ --sol-emph: var(--sol-base01);
+ --sol-muted: var(--sol-base0);
+ --sol-secondary: var(--sol-base1);
+ --sol-shade: var(--sol-base03);
+ --sol-page: var(--sol-panel);
+
+ --sol-tint-soft: 18%;
+ --sol-tint: 25%;
+ --sol-tint-strong: 45%;
+
+ color-scheme: light dark;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --sol-canvas: var(--sol-base03);
+ --sol-panel: var(--sol-base02);
+ --sol-body: var(--sol-base0);
+ --sol-emph: var(--sol-base1);
+ --sol-muted: var(--sol-base00);
+ --sol-secondary: var(--sol-base01);
+ --sol-shade: var(--sol-base3);
+
+ --sol-tint-soft: 30%;
+ --sol-tint: 35%;
+ --sol-tint-strong: 55%;
+ }
+}
+
+body:has(> div#cgit) {
+ margin: 0;
+ background-color: var(--sol-page);
+}
+
+div#cgit {
+ --bg: var(--sol-canvas);
+ --bg-zebra: color-mix(in srgb, var(--sol-panel) 40%, var(--sol-canvas));
+ --bg-hl: var(--sol-panel);
+ --bg-hl2: color-mix(in srgb, var(--sol-panel) 85%, var(--sol-shade));
+ --fg: var(--sol-body);
+ --fg-strong: var(--sol-emph);
+ --fg-mid: var(--sol-body);
+ --fg-mute: var(--sol-muted);
+ --fg-dim: var(--sol-secondary);
+ --fg-faint: color-mix(in srgb, var(--sol-secondary) 65%, var(--sol-canvas));
+ --fg-faintest: color-mix(in srgb, var(--sol-secondary) 45%, var(--sol-canvas));
+ --fg-on-tint: var(--sol-emph);
+ --fg-linenum: var(--sol-secondary);
+ --border: var(--sol-panel);
+ --border-mid: var(--sol-secondary);
+ --border-dark: var(--sol-muted);
+ --border-strong: var(--sol-emph);
+
+ --red: var(--sol-red);
+ --red-mid: color-mix(in srgb, var(--sol-red) 75%, var(--sol-shade));
+ --red-dark: color-mix(in srgb, var(--sol-red) 55%, var(--sol-shade));
+ --green: var(--sol-green);
+ --green-mid: color-mix(in srgb, var(--sol-green) 70%, var(--sol-shade));
+ --green-dark: color-mix(in srgb, var(--sol-green) 50%, var(--sol-shade));
+ --blue: var(--sol-blue);
+ --blue-dark: color-mix(in srgb, var(--sol-blue) 70%, var(--sol-shade));
+
+ --graph-col1: var(--sol-red);
+ --graph-col2: var(--sol-green);
+ --graph-col3: var(--sol-yellow);
+ --graph-col4: var(--sol-blue);
+ --graph-col5: var(--sol-magenta);
+ --graph-col6: var(--sol-cyan);
+
+ --graph-add-bg: var(--sol-green);
+ --graph-rem-bg: var(--sol-red);
+
+ --notes-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint-soft), var(--sol-canvas));
+ --notes-border: var(--sol-yellow);
+ --add-bg: color-mix(in srgb, var(--sol-green) var(--sol-tint), var(--sol-canvas));
+ --add-bg-dark: color-mix(in srgb, var(--sol-green) var(--sol-tint-strong), var(--sol-canvas));
+ --del-bg: color-mix(in srgb, var(--sol-red) var(--sol-tint), var(--sol-canvas));
+ --del-bg-dark: color-mix(in srgb, var(--sol-red) var(--sol-tint-strong), var(--sol-canvas));
+ --changed-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint), var(--sol-canvas));
+ --changed-bg-dark: color-mix(in srgb, var(--sol-yellow) var(--sol-tint-strong), var(--sol-canvas));
+ --hunk-bg: color-mix(in srgb, var(--sol-blue) var(--sol-tint), var(--sol-canvas));
+
+ --deco-branch-bg: color-mix(in srgb, var(--sol-green) var(--sol-tint-soft), var(--sol-canvas));
+ --deco-branch-border: var(--sol-green);
+ --deco-tag-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint-soft), var(--sol-canvas));
+ --deco-tag-border: var(--sol-yellow);
+ --deco-tag-annotated-bg: color-mix(in srgb, var(--sol-orange) var(--sol-tint-soft), var(--sol-canvas));
+ --deco-tag-annotated-border: var(--sol-yellow);
+ --deco-remote-bg: color-mix(in srgb, var(--sol-violet) var(--sol-tint-soft), var(--sol-canvas));
+ --deco-remote-border: var(--sol-violet);
+ --deco-other-bg: color-mix(in srgb, var(--sol-red) var(--sol-tint-soft), var(--sol-canvas));
+ --deco-other-border: var(--sol-red);
+
+ --syn-keyword: var(--sol-emph);
+ --syn-keyword-alt: var(--sol-blue);
+ --syn-type: var(--blue-dark);
+ --syn-string: var(--sol-red);
+ --syn-string-alt: var(--sol-green);
+ --syn-escape: var(--sol-magenta);
+ --syn-comment: var(--sol-secondary);
+ --syn-number: var(--sol-yellow);
+ --syn-preproc: var(--green-mid);
+ --syn-operator: var(--sol-emph);
+ --syn-interpolation: var(--sol-blue);
+ --syn-linenum: var(--sol-secondary);
+ --syn-error: var(--sol-red);
+ --syn-mark-bg: color-mix(in srgb, var(--sol-yellow) var(--sol-tint), var(--sol-canvas));
+}
diff --git a/tests/t0106-diff.sh b/tests/t0106-diff.sh
index 82b645e..e4751e9 100755
--- a/tests/t0106-diff.sh
+++ b/tests/t0106-diff.sh
@@ -16,4 +16,65 @@ test_expect_success 'find added line' '
grep "<div class=.add.>+5</div>" tmp
'
+test_expect_success 'ref picker: from: select present' '
+ grep "<select name=.id2." tmp
+'
+
+test_expect_success 'ref picker: to: select present' '
+ grep "<select name=.id." tmp
+'
+
+test_expect_success 'ref picker: from: label present' '
+ grep "from:" tmp
+'
+
+test_expect_success 'ref picker: to: label present' '
+ grep "to:" tmp
+'
+
+test_expect_success 'ref pickers live in a separate refs panel' '
+ grep "<b>diff refs</b>" tmp &&
+ grep "<b>diff options</b>" tmp
+'
+
+test_expect_success 'ref picker: master branch listed' '
+ grep "master" tmp
+'
+
+test_expect_success 'ref picker: auto-submit on change' '
+ grep "onchange=.this.form.submit()." tmp
+'
+
+test_expect_success 'compare two refs: foo/diff?id=master&id2=HEAD' '
+ cgit_query "url=foo/diff&id=master&id2=HEAD" >tmp
+'
+
+test_expect_success 'diff with explicit refs shows diffstat' '
+ grep "diffstat" tmp
+'
+
+test_expect_success 'diff with oid2 parameter uses it for old ref' '
+ cgit_query "url=foo/diff&id=master&id2=HEAD" >tmp &&
+ grep "from:" tmp
+'
+
+test_expect_success 'ref picker: to: shows master when id=master' '
+ cgit_query "url=foo/diff&id=master" >tmp &&
+ grep "master" tmp
+'
+
+test_expect_success 'ref picker: from: empty when no id2' '
+ cgit_query "url=foo/diff" >tmp &&
+ grep "<select name=.id2." tmp
+'
+
+test_expect_success 'compare initial commit with HEAD' '
+ root=$(git --git-dir="$PWD/repos/foo/.git" rev-list --max-parents=0 HEAD) &&
+ cgit_query "url=foo/diff&id=HEAD&id2=$root" >tmp
+'
+
+test_expect_success 'diff with HEAD and root shows content' '
+ grep "diffstat" tmp
+'
+
test_done
diff --git a/ui-ci.c b/ui-ci.c
index ffd3a65..f2561a2 100644
--- a/ui-ci.c
+++ b/ui-ci.c
@@ -1,6 +1,6 @@
/* ui-ci.c: redirect to an external CI system
*
- * Copyright (C) 2006-2025 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com>
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
diff --git a/ui-diff.c b/ui-diff.c
index 6cfa697..5ee3830 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -1,6 +1,10 @@
/* ui-diff.c: show diff between two blobs
*
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com>
+ *
+ * Modified 2026 by Saya Andy:
+ * Select variable refs from web ui for diff
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -336,16 +340,84 @@ static void filepair_cb(struct diff_filepair *pair)
cgit_ssdiff_footer();
}
+static int print_ref_option(const struct reference *ref, void *cb_data)
+{
+ const char **sel = (const char **)cb_data;
+
+ html_option(ref->name, ref->name, sel[0]);
+ return 0;
+}
+
+static void cgit_print_ref_select(const char *name, const char *selected)
+{
+ /*
+ * Offer every branch and tag so that any two refs can be compared.
+ */
+ html("<select name='");
+ html_attr(name);
+ html("' onchange='this.form.submit();'>");
+ refs_for_each_branch_ref(get_main_ref_store(the_repository),
+ print_ref_option, (void *)&selected);
+ if (ctx.repo->enable_remote_branches)
+ refs_for_each_remote_ref(get_main_ref_store(the_repository),
+ print_ref_option, (void *)&selected);
+ refs_for_each_tag_ref(get_main_ref_store(the_repository),
+ print_ref_option, (void *)&selected);
+ html("</select>");
+}
+
void cgit_print_diff_ctrls(void)
{
int i, curr;
+ html("<form class='diff-ctrls' method='get'>");
+ /*
+ * Emit the same hidden fields as cgit_add_hidden_formfields(), except
+ * id/id2, which are carried by the ref-picker selects below. One
+ * form wraps both panels so any submission carries all controls.
+ */
+ if (!ctx.cfg.virtual_root) {
+ struct strbuf url = STRBUF_INIT;
+ strbuf_addf(&url, "%s/%s", ctx.qry.repo, ctx.qry.page);
+ if (ctx.qry.vpath)
+ strbuf_addf(&url, "/%s", ctx.qry.vpath);
+ html_hidden("url", url.buf);
+ strbuf_release(&url);
+ }
+ if (ctx.qry.head && ctx.repo->defbranch &&
+ strcmp(ctx.qry.head, ctx.repo->defbranch))
+ html_hidden("h", ctx.qry.head);
+ if (ctx.qry.showmsg)
+ html_hidden("showmsg", "1");
+
+ html("<div class='cgit-panel'>");
+ html("<b>diff refs</b>");
+ html("<table>");
+ html("<tr><td colspan='2'/></tr>");
+
+ /* ref pickers: compare any two refs (branches/tags) */
+ {
+ const char *new_ref = ctx.qry.oid ? ctx.qry.oid : ctx.qry.head;
+ const char *old_ref = ctx.qry.oid2;
+
+ html("<tr>");
+ html("<td class='label'>from:</td>");
+ html("<td class='ctrl'>");
+ cgit_print_ref_select("id2", old_ref);
+ html("</td></tr><tr>");
+ html("<td class='label'>to:</td>");
+ html("<td class='ctrl'>");
+ cgit_print_ref_select("id", new_ref);
+ html("</td></tr>");
+ }
+ html("</table>");
+ html("</div>");
+
html("<div class='cgit-panel'>");
html("<b>diff options</b>");
- html("<form method='get'>");
- cgit_add_hidden_formfields(1, 0, ctx.qry.page);
html("<table>");
html("<tr><td colspan='2'/></tr>");
+
html("<tr>");
html("<td class='label'>context:</td>");
html("<td class='ctrl'>");
@@ -379,8 +451,8 @@ void cgit_print_diff_ctrls(void)
html("<tr><td/><td class='ctrl'>");
html("<noscript><input type='submit' value='reload'/></noscript>");
html("</td></tr></table>");
- html("</form>");
html("</div>");
+ html("</form>");
}
void cgit_print_diff(const char *new_rev, const char *old_rev,
diff --git a/ui-shared.c b/ui-shared.c
index 9a9d014..686fde0 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,6 +1,14 @@
/* ui-shared.c: common web output functions
*
* Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2026 Saya Andy <saya.andy@posteo.com>
+ *
+ * Modified 2026 by Saya Andy:
+ * Implement ci link tab
+ * Check repo existence
+ * Replace memrchr() for macOS compatibility
+ * Allow multiple favicons of different mime types to be used
+ * Include favicon.svg as default
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -768,6 +776,51 @@ void cgit_redirect(const char *url, bool permanent)
html("\n\n");
}
+static const struct {
+ const char *ext;
+ const char *mimetype;
+} favicon_mimetypes[] = {
+ { "ico", "image/x-icon" },
+ { "png", "image/png" },
+ { "svg", "image/svg+xml" },
+ { "gif", "image/gif" },
+ { "jpg", "image/jpeg" },
+ { "jpeg", "image/jpeg" },
+ { "webp", "image/webp" },
+ { "avif", "image/avif" },
+};
+
+/* Deduce the mimetype of a favicon from the extension of its url, ignoring
+ * any query string or fragment. Returns NULL for unknown extensions.
+ */
+static const char *favicon_mimetype(const char *url)
+{
+ const char *p, *end, *ext = NULL;
+ size_t len;
+ int i;
+
+ end = strpbrk(url, "?#");
+ if (!end)
+ end = url + strlen(url);
+
+ for (p = url; p < end; p++) {
+ if (*p == '/')
+ ext = NULL;
+ else if (*p == '.')
+ ext = p + 1;
+ }
+ if (!ext)
+ return NULL;
+ len = end - ext;
+
+ for (i = 0; i < ARRAY_SIZE(favicon_mimetypes); i++) {
+ if (strlen(favicon_mimetypes[i].ext) == len &&
+ !strncasecmp(favicon_mimetypes[i].ext, ext, len))
+ return favicon_mimetypes[i].mimetype;
+ }
+ return NULL;
+}
+
static void print_rel_vcs_link(const char *url)
{
html("<link rel='vcs-git' href='");
@@ -809,6 +862,27 @@ static int emit_js_link(struct string_list_item *s, void *arg)
return 0;
}
+static int emit_favicon_link(struct string_list_item *s, void *arg)
+{
+ const char *url = s ? s->string : (const char *)arg;
+ const char *mimetype;
+
+ /* Do not emit anything if favicon= is specified. */
+ if (!*url)
+ return 0;
+
+ html("<link rel='shortcut icon' href='");
+ html_attr(url);
+ mimetype = favicon_mimetype(url);
+ if (mimetype) {
+ html("' type='");
+ html_attr(mimetype);
+ }
+ html("'/>\n");
+
+ return 0;
+}
+
void cgit_print_docstart(void)
{
char *host = cgit_hosturl();
@@ -839,11 +913,12 @@ void cgit_print_docstart(void)
else
emit_js_link(NULL, "/cgit.js");
- if (ctx.cfg.favicon && *ctx.cfg.favicon) {
- html("<link rel='shortcut icon' href='");
- html_attr(ctx.cfg.favicon);
- html("'/>\n");
- }
+ if (ctx.cfg.favicon.items)
+ for_each_string_list(&ctx.cfg.favicon, emit_favicon_link, NULL);
+ else
+ emit_favicon_link(NULL, "/favicon.svg");
+ emit_favicon_link(NULL, "/favicon.ico");
+
if (host && ctx.repo && ctx.qry.head) {
char *fileurl;
struct strbuf sb = STRBUF_INIT;
@@ -884,7 +959,7 @@ void cgit_print_docend(void)
if (ctx.cfg.footer)
html_include(ctx.cfg.footer);
else {
- htmlf("<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> "
+ htmlf("<div class='footer'>generated by <a href='https://sayag.it/cgitext.git/about/'>cgitext %s</a> "
"(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string);
html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)));
html("</div>\n");