-
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
-
528
-
529
-
530
-
531
-
532
-
533
-
534
-
535
-
536
-
537
-
538
-
539
-
540
-
541
-
542
-
543
-
544
-
545
-
546
-
547
-
548
-
549
-
550
-
551
-
552
-
553
-
554
-
555
-
556
-
557
-
558
-
559
-
560
-
561
-
562
-
563
-
564
-
565
-
566
-
567
-
568
-
569
-
570
-
571
-
572
-
573
-
574
-
575
-
576
-
577
-
578
-
579
-
580
-
581
-
582
-
583
-
584
-
585
-
586
-
587
-
588
-
589
-
590
-
591
-
592
-
593
-
594
-
595
-
596
-
597
-
598
-
599
-
600
-
601
-
602
-
603
-
604
-
605
-
606
-
607
-
608
-
609
-
610
-
611
-
612
-
613
-
614
-
615
-
616
-
617
-
618
-
619
-
620
-
621
-
622
-
623
-
624
-
625
-
626
-
627
-
628
-
629
-
630
-
631
-
632
-
633
-
634
-
635
-
636
-
637
-
638
-
639
-
640
-
641
-
642
-
643
-
644
-
645
-
646
-
647
-
648
-
649
-
650
-
651
-
652
-
653
-
654
-
655
-
656
-
657
From a42071e287f3fbf24c1010170f1aaff4781567b6 Mon Sep 17 00:00:00 2001
From: Christian Hesse <mail@eworm.de>
Date: Fri, 16 Dec 2022 18:37:10 +0100
Subject: [PATCH 01/10] ui-log: show ellipsis if detailed commit message is
available
The existence of a detailed commit message may be of interst even
if only the subject is shown by default.
Signed-off-by: Christian Hesse <mail@eworm.de>
---
cgit.css | 9 +++++++++
ui-log.c | 2 ++
2 files changed, 11 insertions(+)
diff --git a/cgit.css b/cgit.css
index d10a24d..1d4634c 100644
--- a/cgit.css
+++ b/cgit.css
@@ -678,6 +678,15 @@ div#cgit div.footer a:hover {
text-decoration: underline;
}
+div#cgit span.msg-avail {
+ color: #000;
+ margin: 0px 0.5em;
+ padding: 0px 0.25em;
+ background-color: #f0f0f0;
+ border: solid 1px #777777;
+ border-radius: 5px;
+}
+
div#cgit a.branch-deco {
color: #000;
margin: 0px 0.5em;
diff --git a/ui-log.c b/ui-log.c
index 9906bbc..ee51bde 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -244,6 +244,8 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
}
cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
oid_to_hex(&commit->object.oid), ctx.qry.vpath);
+ if (!ctx.qry.showmsg && info->msg && *(info->msg))
+ html("<span class='msg-avail'>...</span>");
show_commit_decorations(commit);
html("</td><td>");
cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
--
2.54.0
From c829e631ded662d70fc843b8b83eb7b5d9872cd3 Mon Sep 17 00:00:00 2001
From: Christian Hesse <mail@eworm.de>
Date: Mon, 5 Aug 2024 00:25:49 +0200
Subject: [PATCH 02/10] ui-log: show commit message in tooltip
... now that we have the ellipsis to indicate it is available.
Signed-off-by: Christian Hesse <mail@eworm.de>
---
cgit.css | 18 ++++++++++++++++++
ui-log.c | 7 +++++--
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/cgit.css b/cgit.css
index 1d4634c..092883f 100644
--- a/cgit.css
+++ b/cgit.css
@@ -687,6 +687,24 @@ div#cgit span.msg-avail {
border-radius: 5px;
}
+div#cgit span.msg-avail span.msg-tooltip {
+ color: #000;
+ margin: 0px 0.5em;
+ padding: 0px 0.25em;
+ background-color: #f0f0f0;
+ border: solid 1px #777777;
+ border-radius: 5px;
+ box-shadow: 2px 2px 7px rgba(100,100,100,0.75);
+ visibility: hidden;
+ position: absolute;
+ white-space: pre;
+ z-index: 1;
+}
+
+div#cgit span.msg-avail:hover span.msg-tooltip {
+ visibility: visible;
+}
+
div#cgit a.branch-deco {
color: #000;
margin: 0px 0.5em;
diff --git a/ui-log.c b/ui-log.c
index ee51bde..4ec5817 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -244,8 +244,11 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
}
cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
oid_to_hex(&commit->object.oid), ctx.qry.vpath);
- if (!ctx.qry.showmsg && info->msg && *(info->msg))
- html("<span class='msg-avail'>...</span>");
+ if (!ctx.qry.showmsg && info->msg && *(info->msg)) {
+ html("<span class='msg-avail'>...<span class='msg-tooltip'>");
+ html_txt(info->msg);
+ html("</span></span>");
+ }
show_commit_decorations(commit);
html("</td><td>");
cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
--
2.54.0
From 8c1d1cd5dee361084e28e7561e903eb5fbf5a544 Mon Sep 17 00:00:00 2001
From: Christian Hesse <mail@eworm.de>
Date: Wed, 16 Oct 2024 11:59:42 +0200
Subject: [PATCH 03/10] ui-log: build ellipsis from bullets
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
These look cleaner... IMHO. 😜
Signed-off-by: Christian Hesse <mail@eworm.de>
---
ui-log.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui-log.c b/ui-log.c
index 4ec5817..434ff8d 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -245,7 +245,7 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
oid_to_hex(&commit->object.oid), ctx.qry.vpath);
if (!ctx.qry.showmsg && info->msg && *(info->msg)) {
- html("<span class='msg-avail'>...<span class='msg-tooltip'>");
+ html("<span class='msg-avail'>•••<span class='msg-tooltip'>");
html_txt(info->msg);
html("</span></span>");
}
--
2.54.0
From f1861553f3bfd48f4fc1559b23a5cc1b0cc7bda2 Mon Sep 17 00:00:00 2001
From: Christian Hesse <mail@eworm.de>
Date: Sat, 29 Dec 2018 00:13:54 +0100
Subject: [PATCH 04/10] ui-tree: provide link to about page for pretty
formatting
We have an ongoing discussion about display filters and pretty
formatting in tree view. How about providing a link to about page
for pretty formatting?
Signed-off-by: Christian Hesse <mail@eworm.de>
---
ui-shared.c | 6 ++++++
ui-shared.h | 3 +++
ui-tree.c | 3 +++
3 files changed, 12 insertions(+)
diff --git a/ui-shared.c b/ui-shared.c
index df52a9b..0734711 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -348,6 +348,12 @@ void cgit_tree_link(const char *name, const char *title, const char *class,
reporevlink("tree", name, title, class, head, rev, path);
}
+void cgit_about_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
+{
+ reporevlink("about", name, title, class, head, rev, path);
+}
+
void cgit_plain_link(const char *name, const char *title, const char *class,
const char *head, const char *rev, const char *path)
{
diff --git a/ui-shared.h b/ui-shared.h
index 2a3a7f5..6a2c6e2 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -24,6 +24,9 @@ extern void cgit_tag_link(const char *name, const char *title,
extern void cgit_tree_link(const char *name, const char *title,
const char *class, const char *head,
const char *rev, const char *path);
+extern void cgit_about_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path);
extern void cgit_plain_link(const char *name, const char *title,
const char *class, const char *head,
const char *rev, const char *path);
diff --git a/ui-tree.c b/ui-tree.c
index 5ac8c9a..851074e 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -112,6 +112,9 @@ static void print_object(const struct object_id *oid, const char *path, const ch
cgit_print_layout_start();
htmlf("blob: %s (", oid_to_hex(oid));
+ cgit_about_link("about", NULL, NULL, ctx.qry.head,
+ rev, path);
+ html(") (");
cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
rev, path);
if (ctx.repo->enable_blame && !is_binary) {
--
2.54.0
From 4c037918a2aab30e47cacfe1b296a7927137ace4 Mon Sep 17 00:00:00 2001
From: Christian Hesse <mail@eworm.de>
Date: Wed, 2 Jan 2019 09:20:40 +0100
Subject: [PATCH 05/10] ui-tree: add about link in tree view list
Signed-off-by: Christian Hesse <mail@eworm.de>
---
ui-tree.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ui-tree.c b/ui-tree.c
index 851074e..542c523 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -272,6 +272,9 @@ static int ls_item(const struct object_id *oid, struct strbuf *base,
if (!S_ISGITLINK(mode))
cgit_plain_link("plain", NULL, "button", ctx.qry.head,
walk_tree_ctx->curr_rev, fullpath.buf);
+ if (!S_ISDIR(mode))
+ cgit_about_link("about", NULL, "button", ctx.qry.head,
+ walk_tree_ctx->curr_rev, fullpath.buf);
if (!S_ISDIR(mode) && ctx.repo->enable_blame)
cgit_blame_link("blame", NULL, "button", ctx.qry.head,
walk_tree_ctx->curr_rev, fullpath.buf);
--
2.54.0
From 1854c18658707741c5bf0b5eaca554e083a3729b Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Wed, 19 Mar 2014 02:58:41 -0600
Subject: [PATCH 06/10] steal kernel.org's libravatar lua
Signed-off-by: Christian Hesse <mail@eworm.de>
---
cgit.css | 28 ++++++++++++++++++++++++++++
filters/email-libravatar-hover.lua | 25 +++++++++++++++++++++++++
2 files changed, 53 insertions(+)
create mode 100644 filters/email-libravatar-hover.lua
diff --git a/cgit.css b/cgit.css
index 092883f..dab5977 100644
--- a/cgit.css
+++ b/cgit.css
@@ -934,3 +934,31 @@ div#cgit table.ssdiff td.space {
div#cgit table.ssdiff td.space div {
min-height: 3em;
}
+div#cgit span.libravatar img.onhover {
+ display: none;
+ border: 1px solid gray;
+ padding: 0px;
+ -webkit-border-radius: 4px;
+ -moz-border-radius: 4px;
+ border-radius: 4px;
+ width: 128px;
+ height: 128px;
+}
+
+div#cgit span.libravatar img.inline {
+ -webkit-border-radius: 3px;
+ -moz-border-radius: 3px;
+ border-radius: 3px;
+ width: 13px;
+ height: 13px;
+ margin-right: 0.2em;
+ opacity: 0.6;
+}
+
+div#cgit span.libravatar:hover > img.onhover {
+ display: block;
+ position: absolute;
+ margin-left: 1.5em;
+ background-color: #eeeeee;
+ box-shadow: 2px 2px 7px rgba(100,100,100,0.75);
+}
diff --git a/filters/email-libravatar-hover.lua b/filters/email-libravatar-hover.lua
new file mode 100644
index 0000000..c14b618
--- /dev/null
+++ b/filters/email-libravatar-hover.lua
@@ -0,0 +1,25 @@
+local digest = require("openssl.digest")
+
+function md5_hex(input)
+ local b = digest.new("md5"):final(input)
+ local x = ""
+ for i = 1, #b do
+ x = x .. string.format("%.2x", string.byte(b, i))
+ end
+ return x
+end
+
+function filter_open(email, page)
+ buffer = ""
+ hexdigest = md5_hex(email:sub(2, -2):lower())
+end
+
+function filter_close()
+ baseurl = os.getenv("HTTPS") and "https://seccdn.libravatar.org/" or "http://cdn.libravatar.org/"
+ html("<span class='libravatar'><img class='inline' src='" .. baseurl .. "avatar/" .. hexdigest .. "?s=13&d=retro' /><img class='onhover' src='" .. baseurl .. "avatar/" .. hexdigest .. "?s=128&d=retro' /></span>" .. buffer)
+ return 0
+end
+
+function filter_write(str)
+ buffer = buffer .. str
+end
--
2.54.0
From 0f782947ed10ba7341d95fcf3de5095c3dc2ef8c Mon Sep 17 00:00:00 2001
From: Eric Wong <e@80x24.org>
Date: Thu, 21 May 2026 15:04:30 +0200
Subject: [PATCH 07/10] ui-log: improve decoration display for browsers without
CSS
Text-based browsers without CSS support show all the decorations
bunched together without spacing. Rely on a whitespace instead
of CSS support.
---
cgit.css | 1 -
ui-log.c | 4 ++++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/cgit.css b/cgit.css
index dab5977..d669e47 100644
--- a/cgit.css
+++ b/cgit.css
@@ -755,7 +755,6 @@ div#cgit div.commit-subject a.tag-deco,
div#cgit div.commit-subject a.tag-annotated-deco,
div#cgit div.commit-subject a.remote-deco,
div#cgit div.commit-subject a.deco {
- margin-left: 1em;
font-size: 75%;
}
diff --git a/ui-log.c b/ui-log.c
index 434ff8d..b4c895c 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -77,11 +77,13 @@ void show_commit_decorations(struct commit *commit)
* don't display anything. */
break;
case DECORATION_REF_LOCAL:
+ html(" ");
cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
ctx.qry.vpath, 0, NULL, NULL,
ctx.qry.showmsg, 0);
break;
case DECORATION_REF_TAG:
+ html(" ");
if (!refs_read_ref(get_main_ref_store(the_repository), deco->name, &oid_tag) &&
!peel_object(the_repository, &oid_tag, &peeled, PEEL_OBJECT_VERIFY_TAGGED_OBJECT_TYPE))
is_annotated = !oideq(&oid_tag, &peeled);
@@ -90,12 +92,14 @@ void show_commit_decorations(struct commit *commit)
case DECORATION_REF_REMOTE:
if (!ctx.repo->enable_remote_branches)
break;
+ html(" ");
cgit_log_link(buf, NULL, "remote-deco", NULL,
oid_to_hex(&commit->object.oid),
ctx.qry.vpath, 0, NULL, NULL,
ctx.qry.showmsg, 0);
break;
default:
+ html(" ");
cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
oid_to_hex(&commit->object.oid),
ctx.qry.vpath);
--
2.54.0
From 1cd637cd1f637aa8aa1c2d416ae01ee31b3a691c Mon Sep 17 00:00:00 2001
From: Eric Wong <e@80x24.org>
Date: Thu, 21 May 2026 15:11:10 +0200
Subject: [PATCH 08/10] use buffered stdio
Our generation of HTML triggers many small write(2) syscalls
which is inefficient.
Time output on a horrible query against my git.git mirror
shows significant performance improvement:
QUERY_STRING='id=2b93bfac0f5bcabbf60f174f4e7bfa9e318e64d5&id2=d6da71a9d16b8cf27f9d8f90692d3625c849cbc8'
PATH_INFO=/mirrors/git.git/diff
export QUERY_STRING PATH_INFO
time ./cgit >/dev/null
Before:
real 0m1.585s
user 0m0.904s
sys 0m0.658s
After:
real 0m0.750s
user 0m0.666s
sys 0m0.076s
Signed-off-by: Eric Wong <e@80x24.org>
Signed-off-by: Alyssa Ross <hi@alyssa.is>
---
cache.c | 7 +++++++
cgit.c | 2 +-
filter.c | 22 +++++++++++++++++++++-
html.c | 2 +-
ui-snapshot.c | 3 +++
5 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/cache.c b/cache.c
index e70af13..13cceac 100644
--- a/cache.c
+++ b/cache.c
@@ -272,6 +272,13 @@ static int process_slot(struct cache_slot *slot)
{
int err;
+ /*
+ * Make sure any buffered data is flushed before we redirect,
+ * do sendfile(2) or write(2)
+ */
+ if (fflush(stdout))
+ return errno;
+
err = open_slot(slot);
if (!err && slot->match) {
if (is_expired(slot)) {
diff --git a/cgit.c b/cgit.c
index ca318e8..3dc55c3 100644
--- a/cgit.c
+++ b/cgit.c
@@ -684,7 +684,7 @@ static inline void authenticate_post(void)
len = MAX_AUTHENTICATION_POST_BYTES;
if ((len = read(STDIN_FILENO, buffer, len)) < 0)
die_errno("Could not read POST from stdin");
- if (write(STDOUT_FILENO, buffer, len) < 0)
+ if (fwrite(buffer, 1, len, stdout) < len)
die_errno("Could not write POST to stdout");
cgit_close_filter(ctx.cfg.auth_filter);
exit(0);
diff --git a/filter.c b/filter.c
index c778d05..781f8e3 100644
--- a/filter.c
+++ b/filter.c
@@ -48,6 +48,7 @@ static int open_exec_filter(struct cgit_filter *base, va_list ap)
for (i = 0; i < filter->base.argument_count; i++)
filter->argv[i + 1] = va_arg(ap, char *);
+ chk_zero(fflush(stdout), "unable to flush STDOUT");
filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
"Unable to duplicate STDOUT");
chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
@@ -71,6 +72,7 @@ static int close_exec_filter(struct cgit_filter *base)
struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
int i, exit_status = 0;
+ chk_zero(fflush(stdout), "unable to flush STDOUT");
chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
"Unable to restore STDOUT");
close(filter->old_stdout);
@@ -143,17 +145,32 @@ void cgit_init_filters(void)
#endif
#ifndef NO_LUA
-static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
+static size_t (*libc_fwrite)(const void *buf, size_t size, size_t n, FILE *);
+static ssize_t (*libc_write)(int fd, const void *buf, size_t size);
static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
static struct cgit_filter *current_write_filter = NULL;
void cgit_init_filters(void)
{
+ /*
+ * we need to wrap both functions since the Lua filter may
+ * have code which calls write(2) directly, bypassing fwrite(3)
+ */
+ libc_fwrite = dlsym(RTLD_NEXT, "fwrite");
+ if (!libc_fwrite)
+ die("Could not locate libc's write function");
libc_write = dlsym(RTLD_NEXT, "write");
if (!libc_write)
die("Could not locate libc's write function");
}
+size_t fwrite(const void *buf, size_t size, size_t n, FILE *f)
+{
+ if (f != stdout || !filter_write)
+ return libc_fwrite(buf, size, n, f);
+ return filter_write(current_write_filter, buf, size * n);
+}
+
ssize_t write(int fd, const void *buf, size_t count)
{
if (fd != STDOUT_FILENO || !filter_write)
@@ -305,6 +322,9 @@ static int open_lua_filter(struct cgit_filter *base, va_list ap)
struct lua_filter *filter = (struct lua_filter *)base;
int i;
+ if (fflush(stdout))
+ return 1;
+
if (init_lua_filter(filter))
return 1;
diff --git a/html.c b/html.c
index 0bac34b..ced781a 100644
--- a/html.c
+++ b/html.c
@@ -80,7 +80,7 @@ char *fmtalloc(const char *format, ...)
void html_raw(const char *data, size_t size)
{
- if (write(STDOUT_FILENO, data, size) != size)
+ if (fwrite(data, 1, size, stdout) != size)
die_errno("write error on html output");
}
diff --git a/ui-snapshot.c b/ui-snapshot.c
index d157222..b3039d9 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -39,6 +39,9 @@ static int write_archive_type(const char *format, const char *hex, const char *p
/* strvec guarantees a trailing NULL entry. */
memcpy(nargv, argv.v, sizeof(char *) * (argv.nr + 1));
+ if (fflush(stdout))
+ return errno;
+
result = write_archive(argv.nr, nargv, NULL, the_repository, NULL, 0);
strvec_clear(&argv);
free(nargv);
--
2.54.0
From 55f8359c0b18e6f2c182fd14132d1d58fb39b6d1 Mon Sep 17 00:00:00 2001
From: Alyssa Ross <hi@alyssa.is>
Date: Thu, 21 May 2026 14:17:50 +0200
Subject: [PATCH 09/10] ui-shared: add "this commit" option to switch form
Branches are grouped into their own section to make the "this commit"
option visually distinct.
Adding this option will result in two options being marked as selected
if a branch has the same name as a commit oid. But that would cause
all sorts of other problems anyway (attempting to switch to the branch
would actually give you the commit, etc.), so let's not worry about
that.
A "permalink" link on the blob view next to the "plain" link would
probably be more discoverable, but that would only work for the blob
view. The switch UI is visible everywhere.
Signed-off-by: Alyssa Ross <hi@alyssa.is>
---
ui-shared.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/ui-shared.c b/ui-shared.c
index 0734711..9b602cc 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -947,6 +947,15 @@ void cgit_add_clone_urls(void (*fn)(const char *))
add_clone_urls(fn, ctx.cfg.clone_prefix, ctx.repo->url);
}
+static int print_this_commit_option(void)
+{
+ struct object_id oid;
+ if (repo_get_oid(the_repository, ctx.qry.head, &oid))
+ return 1;
+ html_option(oid_to_hex(&oid), "this commit", ctx.qry.head);
+ return 0;
+}
+
static int print_branch_option(const struct reference *ref, void *cb_data)
{
char *name = (char *)ref->name;
@@ -1053,11 +1062,14 @@ static void print_header(void)
html("<form method='get'>\n");
cgit_add_hidden_formfields(0, 1, ctx.qry.page);
html("<select name='h' onchange='this.form.submit();'>\n");
+ print_this_commit_option();
+ html("<optgroup label='branches'>");
refs_for_each_branch_ref(get_main_ref_store(the_repository),
print_branch_option, ctx.qry.head);
if (ctx.repo->enable_remote_branches)
refs_for_each_remote_ref(get_main_ref_store(the_repository),
print_branch_option, ctx.qry.head);
+ html("</optgroup>");
html("</select> ");
html("<input type='submit' value='switch'/>");
html("</form>");
--
2.54.0
From c9f91124f9462d8eeb0a9d2ddeff586807413fc0 Mon Sep 17 00:00:00 2001
From: Baptiste Jonglez <git@bitsofnetworks.org>
Date: Fri, 27 Feb 2026 16:51:11 +0100
Subject: [PATCH 10/10] cgit: Fix guessing default branch
Even when HEAD is set to "main" in a repository, cgit still uses the
"master" branch to detect the age of the last commit.
Example:
$ cat repositories/openwrt/openwrt.git/HEAD
ref: refs/heads/main
$ stat repositories/openwrt/openwrt.git/refs/heads/master
Modify: 2024-02-22 23:00:01.285731731 +0000
$ stat repositories/openwrt/openwrt.git/refs/heads/main
Modify: 2026-02-27 15:33:49.506017172 +0000
In this situation, cgit displays "25 months" for this repository on the index page.
I guess the internal format for HEAD has changed at some point, so it
needs to be taken into account when parsing it in cgit.
Signed-off-by: Baptiste Jonglez <git@bitsofnetworks.org>
---
cgit.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cgit.c b/cgit.c
index 3dc55c3..2efbb2c 100644
--- a/cgit.c
+++ b/cgit.c
@@ -496,7 +496,7 @@ static char *guess_defbranch(void)
ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
"HEAD", 0, &oid, NULL);
- if (!ref || !skip_prefix(ref, "refs/heads/", &refname))
+ if (!ref || !skip_prefix(ref, "ref: refs/heads/", &refname))
return "master";
return xstrdup(refname);
}
--
2.54.0