aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--cgit.c42
-rw-r--r--cgit.css358
-rw-r--r--cgit.h8
-rw-r--r--cgitrc.5.txt67
-rw-r--r--cmd.c44
-rw-r--r--cmd.h3
-rwxr-xr-xfilters/commit-links.sh18
-rwxr-xr-xfilters/syntax-highlighting.sh11
-rw-r--r--html.c8
-rw-r--r--html.h1
-rw-r--r--shared.c29
-rwxr-xr-xtests/setup.sh1
-rwxr-xr-xtests/t0102-summary.sh6
-rw-r--r--ui-commit.c10
-rw-r--r--ui-diff.c81
-rw-r--r--ui-diff.h4
-rw-r--r--ui-log.c29
-rw-r--r--ui-repolist.c15
-rw-r--r--ui-stats.c51
-rw-r--r--ui-summary.c2
21 files changed, 507 insertions, 287 deletions
diff --git a/Makefile b/Makefile
index 6de072a..eac24ad 100644
--- a/Makefile
+++ b/Makefile
@@ -198,9 +198,9 @@ install-pdf: doc-pdf
198 $(INSTALL) -m 0644 $(DOC_PDF) $(DESTDIR)$(pdfdir) 198 $(INSTALL) -m 0644 $(DOC_PDF) $(DESTDIR)$(pdfdir)
199 199
200uninstall: 200uninstall:
201 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 201 rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
202 rm -f $(CGIT_DATA_PATH)/cgit.css 202 rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
203 rm -f $(CGIT_DATA_PATH)/cgit.png 203 rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
204 204
205uninstall-doc: uninstall-man uninstall-html uninstall-pdf 205uninstall-doc: uninstall-man uninstall-html uninstall-pdf
206 206
diff --git a/cgit.c b/cgit.c
index 435ce5a..1d50129 100644
--- a/cgit.c
+++ b/cgit.c
@@ -26,14 +26,27 @@ void add_mimetype(const char *name, const char *value)
26 item->util = xstrdup(value); 26 item->util = xstrdup(value);
27} 27}
28 28
29struct cgit_filter *new_filter(const char *cmd, int extra_args) 29struct cgit_filter *new_filter(const char *cmd, filter_type filtertype)
30{ 30{
31 struct cgit_filter *f; 31 struct cgit_filter *f;
32 int args_size = 0; 32 int args_size = 0;
33 int extra_args;
33 34
34 if (!cmd || !cmd[0]) 35 if (!cmd || !cmd[0])
35 return NULL; 36 return NULL;
36 37
38 switch (filtertype) {
39 case SOURCE:
40 extra_args = 1;
41 break;
42
43 case ABOUT:
44 case COMMIT:
45 default:
46 extra_args = 0;
47 break;
48 }
49
37 f = xmalloc(sizeof(struct cgit_filter)); 50 f = xmalloc(sizeof(struct cgit_filter));
38 f->cmd = xstrdup(cmd); 51 f->cmd = xstrdup(cmd);
39 args_size = (2 + extra_args) * sizeof(char *); 52 args_size = (2 + extra_args) * sizeof(char *);
@@ -83,11 +96,11 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
83 repo->logo_link = xstrdup(value); 96 repo->logo_link = xstrdup(value);
84 else if (ctx.cfg.enable_filter_overrides) { 97 else if (ctx.cfg.enable_filter_overrides) {
85 if (!strcmp(name, "about-filter")) 98 if (!strcmp(name, "about-filter"))
86 repo->about_filter = new_filter(value, 0); 99 repo->about_filter = new_filter(value, ABOUT);
87 else if (!strcmp(name, "commit-filter")) 100 else if (!strcmp(name, "commit-filter"))
88 repo->commit_filter = new_filter(value, 0); 101 repo->commit_filter = new_filter(value, COMMIT);
89 else if (!strcmp(name, "source-filter")) 102 else if (!strcmp(name, "source-filter"))
90 repo->source_filter = new_filter(value, 1); 103 repo->source_filter = new_filter(value, SOURCE);
91 } 104 }
92} 105}
93 106
@@ -147,6 +160,8 @@ void config_cb(const char *name, const char *value)
147 ctx.cfg.enable_filter_overrides = atoi(value); 160 ctx.cfg.enable_filter_overrides = atoi(value);
148 else if (!strcmp(name, "enable-gitweb-owner")) 161 else if (!strcmp(name, "enable-gitweb-owner"))
149 ctx.cfg.enable_gitweb_owner = atoi(value); 162 ctx.cfg.enable_gitweb_owner = atoi(value);
163 else if (!strcmp(name, "enable-http-clone"))
164 ctx.cfg.enable_http_clone = atoi(value);
150 else if (!strcmp(name, "enable-index-links")) 165 else if (!strcmp(name, "enable-index-links"))
151 ctx.cfg.enable_index_links = atoi(value); 166 ctx.cfg.enable_index_links = atoi(value);
152 else if (!strcmp(name, "enable-commit-graph")) 167 else if (!strcmp(name, "enable-commit-graph"))
@@ -178,9 +193,9 @@ void config_cb(const char *name, const char *value)
178 else if (!strcmp(name, "cache-dynamic-ttl")) 193 else if (!strcmp(name, "cache-dynamic-ttl"))
179 ctx.cfg.cache_dynamic_ttl = atoi(value); 194 ctx.cfg.cache_dynamic_ttl = atoi(value);
180 else if (!strcmp(name, "about-filter")) 195 else if (!strcmp(name, "about-filter"))
181 ctx.cfg.about_filter = new_filter(value, 0); 196 ctx.cfg.about_filter = new_filter(value, ABOUT);
182 else if (!strcmp(name, "commit-filter")) 197 else if (!strcmp(name, "commit-filter"))
183 ctx.cfg.commit_filter = new_filter(value, 0); 198 ctx.cfg.commit_filter = new_filter(value, COMMIT);
184 else if (!strcmp(name, "embedded")) 199 else if (!strcmp(name, "embedded"))
185 ctx.cfg.embedded = atoi(value); 200 ctx.cfg.embedded = atoi(value);
186 else if (!strcmp(name, "max-atom-items")) 201 else if (!strcmp(name, "max-atom-items"))
@@ -210,7 +225,7 @@ void config_cb(const char *name, const char *value)
210 else if (!strcmp(name, "section-from-path")) 225 else if (!strcmp(name, "section-from-path"))
211 ctx.cfg.section_from_path = atoi(value); 226 ctx.cfg.section_from_path = atoi(value);
212 else if (!strcmp(name, "source-filter")) 227 else if (!strcmp(name, "source-filter"))
213 ctx.cfg.source_filter = new_filter(value, 1); 228 ctx.cfg.source_filter = new_filter(value, SOURCE);
214 else if (!strcmp(name, "summary-log")) 229 else if (!strcmp(name, "summary-log"))
215 ctx.cfg.summary_log = atoi(value); 230 ctx.cfg.summary_log = atoi(value);
216 else if (!strcmp(name, "summary-branches")) 231 else if (!strcmp(name, "summary-branches"))
@@ -229,6 +244,8 @@ void config_cb(const char *name, const char *value)
229 ctx.cfg.robots = xstrdup(value); 244 ctx.cfg.robots = xstrdup(value);
230 else if (!strcmp(name, "clone-prefix")) 245 else if (!strcmp(name, "clone-prefix"))
231 ctx.cfg.clone_prefix = xstrdup(value); 246 ctx.cfg.clone_prefix = xstrdup(value);
247 else if (!strcmp(name, "clone-url"))
248 ctx.cfg.clone_url = xstrdup(value);
232 else if (!strcmp(name, "local-time")) 249 else if (!strcmp(name, "local-time"))
233 ctx.cfg.local_time = atoi(value); 250 ctx.cfg.local_time = atoi(value);
234 else if (!prefixcmp(name, "mimetype.")) 251 else if (!prefixcmp(name, "mimetype."))
@@ -313,6 +330,7 @@ static void prepare_context(struct cgit_context *ctx)
313 ctx->cfg.logo = "/cgit.png"; 330 ctx->cfg.logo = "/cgit.png";
314 ctx->cfg.local_time = 0; 331 ctx->cfg.local_time = 0;
315 ctx->cfg.enable_gitweb_owner = 1; 332 ctx->cfg.enable_gitweb_owner = 1;
333 ctx->cfg.enable_http_clone = 1;
316 ctx->cfg.enable_tree_linenumbers = 1; 334 ctx->cfg.enable_tree_linenumbers = 1;
317 ctx->cfg.max_repo_count = 50; 335 ctx->cfg.max_repo_count = 50;
318 ctx->cfg.max_commit_count = 50; 336 ctx->cfg.max_commit_count = 50;
@@ -444,7 +462,7 @@ static int prepare_repo_cmd(struct cgit_context *ctx)
444 tmp = xstrdup(ctx->qry.head); 462 tmp = xstrdup(ctx->qry.head);
445 ctx->qry.head = ctx->repo->defbranch; 463 ctx->qry.head = ctx->repo->defbranch;
446 ctx->page.status = 404; 464 ctx->page.status = 404;
447 ctx->page.statusmsg = "not found"; 465 ctx->page.statusmsg = "Not found";
448 cgit_print_http_headers(ctx); 466 cgit_print_http_headers(ctx);
449 cgit_print_docstart(ctx); 467 cgit_print_docstart(ctx);
450 cgit_print_pageheader(ctx); 468 cgit_print_pageheader(ctx);
@@ -452,6 +470,7 @@ static int prepare_repo_cmd(struct cgit_context *ctx)
452 cgit_print_docend(); 470 cgit_print_docend();
453 return 1; 471 return 1;
454 } 472 }
473 cgit_prepare_repo_env(ctx->repo);
455 return 0; 474 return 0;
456} 475}
457 476
@@ -463,6 +482,8 @@ static void process_request(void *cbdata)
463 cmd = cgit_get_cmd(ctx); 482 cmd = cgit_get_cmd(ctx);
464 if (!cmd) { 483 if (!cmd) {
465 ctx->page.title = "cgit error"; 484 ctx->page.title = "cgit error";
485 ctx->page.status = 404;
486 ctx->page.statusmsg = "Not found";
466 cgit_print_http_headers(ctx); 487 cgit_print_http_headers(ctx);
467 cgit_print_docstart(ctx); 488 cgit_print_docstart(ctx);
468 cgit_print_pageheader(ctx); 489 cgit_print_pageheader(ctx);
@@ -471,6 +492,11 @@ static void process_request(void *cbdata)
471 return; 492 return;
472 } 493 }
473 494
495 if (!ctx->cfg.enable_http_clone && cmd->is_clone) {
496 html_status(404, "Not found", 0);
497 return;
498 }
499
474 /* If cmd->want_vpath is set, assume ctx->qry.path contains a "virtual" 500 /* If cmd->want_vpath is set, assume ctx->qry.path contains a "virtual"
475 * in-project path limit to be made available at ctx->qry.vpath. 501 * in-project path limit to be made available at ctx->qry.vpath.
476 * Otherwise, no path limit is in effect (ctx->qry.vpath = NULL). 502 * Otherwise, no path limit is in effect (ctx->qry.vpath = NULL).
diff --git a/cgit.css b/cgit.css
index 8f409c3..e06c261 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,4 +1,4 @@
1body, table, form { 1body, div#cgit table, div#cgit form {
2 padding: 0em; 2 padding: 0em;
3 margin: 0em; 3 margin: 0em;
4} 4}
@@ -11,39 +11,40 @@ body {
11 padding: 4px; 11 padding: 4px;
12} 12}
13 13
14a { 14div#cgit a {
15 color: blue; 15 color: blue;
16 text-decoration: none; 16 text-decoration: none;
17} 17}
18 18
19a:hover { 19div#cgit a:hover {
20 text-decoration: underline; 20 text-decoration: underline;
21} 21}
22 22
23table { 23div#cgit table {
24 border-collapse: collapse; 24 border-collapse: collapse;
25} 25}
26 26
27table#header { 27div#cgit table#header {
28 width: 100%; 28 width: 100%;
29 margin-bottom: 1em; 29 margin-bottom: 1em;
30} 30}
31 31
32table#header td.logo { 32div#cgit table#header td.logo {
33 width: 96px; 33 width: 96px;
34 vertical-align: top;
34} 35}
35 36
36table#header td.main { 37div#cgit table#header td.main {
37 font-size: 250%; 38 font-size: 250%;
38 padding-left: 10px; 39 padding-left: 10px;
39 white-space: nowrap; 40 white-space: nowrap;
40} 41}
41 42
42table#header td.main a { 43div#cgit table#header td.main a {
43 color: #000; 44 color: #000;
44} 45}
45 46
46table#header td.form { 47div#cgit table#header td.form {
47 text-align: right; 48 text-align: right;
48 vertical-align: bottom; 49 vertical-align: bottom;
49 padding-right: 1em; 50 padding-right: 1em;
@@ -51,19 +52,19 @@ table#header td.form {
51 white-space: nowrap; 52 white-space: nowrap;
52} 53}
53 54
54table#header td.form form, 55div#cgit table#header td.form form,
55table#header td.form input, 56div#cgit table#header td.form input,
56table#header td.form select { 57div#cgit table#header td.form select {
57 font-size: 90%; 58 font-size: 90%;
58} 59}
59 60
60table#header td.sub { 61div#cgit table#header td.sub {
61 color: #777; 62 color: #777;
62 border-top: solid 1px #ccc; 63 border-top: solid 1px #ccc;
63 padding-left: 10px; 64 padding-left: 10px;
64} 65}
65 66
66table.tabs { 67div#cgit table.tabs {
67 border-bottom: solid 3px #ccc; 68 border-bottom: solid 3px #ccc;
68 border-collapse: collapse; 69 border-collapse: collapse;
69 margin-top: 2em; 70 margin-top: 2em;
@@ -71,74 +72,74 @@ table.tabs {
71 width: 100%; 72 width: 100%;
72} 73}
73 74
74table.tabs td { 75div#cgit table.tabs td {
75 padding: 0px 1em; 76 padding: 0px 1em;
76 vertical-align: bottom; 77 vertical-align: bottom;
77} 78}
78 79
79table.tabs td a { 80div#cgit table.tabs td a {
80 padding: 2px 0.75em; 81 padding: 2px 0.75em;
81 color: #777; 82 color: #777;
82 font-size: 110%; 83 font-size: 110%;
83} 84}
84 85
85table.tabs td a.active { 86div#cgit table.tabs td a.active {
86 color: #000; 87 color: #000;
87 background-color: #ccc; 88 background-color: #ccc;
88} 89}
89 90
90table.tabs td.form { 91div#cgit table.tabs td.form {
91 text-align: right; 92 text-align: right;
92} 93}
93 94
94table.tabs td.form form { 95div#cgit table.tabs td.form form {
95 padding-bottom: 2px; 96 padding-bottom: 2px;
96 font-size: 90%; 97 font-size: 90%;
97 white-space: nowrap; 98 white-space: nowrap;
98} 99}
99 100
100table.tabs td.form input, 101div#cgit table.tabs td.form input,
101table.tabs td.form select { 102div#cgit table.tabs td.form select {
102 font-size: 90%; 103 font-size: 90%;
103} 104}
104 105
105div.path { 106div#cgit div.path {
106 margin: 0px; 107 margin: 0px;
107 padding: 5px 2em 2px 2em; 108 padding: 5px 2em 2px 2em;
108 color: #000; 109 color: #000;
109 background-color: #eee; 110 background-color: #eee;
110} 111}
111 112
112div.content { 113div#cgit div.content {
113 margin: 0px; 114 margin: 0px;
114 padding: 2em; 115 padding: 2em;
115 border-bottom: solid 3px #ccc; 116 border-bottom: solid 3px #ccc;
116} 117}
117 118
118 119
119table.list { 120div#cgit table.list {
120 width: 100%; 121 width: 100%;
121 border: none; 122 border: none;
122 border-collapse: collapse; 123 border-collapse: collapse;
123} 124}
124 125
125table.list tr { 126div#cgit table.list tr {
126 background: white; 127 background: white;
127} 128}
128 129
129table.list tr.logheader { 130div#cgit table.list tr.logheader {
130 background: #eee; 131 background: #eee;
131} 132}
132 133
133table.list tr:hover { 134div#cgit table.list tr:hover {
134 background: #eee; 135 background: #eee;
135} 136}
136 137
137table.list tr.nohover:hover { 138div#cgit table.list tr.nohover:hover {
138 background: white; 139 background: white;
139} 140}
140 141
141table.list th { 142div#cgit table.list th {
142 font-weight: bold; 143 font-weight: bold;
143 /* color: #888; 144 /* color: #888;
144 border-top: dashed 1px #888; 145 border-top: dashed 1px #888;
@@ -148,93 +149,93 @@ table.list th {
148 vertical-align: baseline; 149 vertical-align: baseline;
149} 150}
150 151
151table.list td { 152div#cgit table.list td {
152 border: none; 153 border: none;
153 padding: 0.1em 0.5em 0.1em 0.5em; 154 padding: 0.1em 0.5em 0.1em 0.5em;
154} 155}
155 156
156table.list td.commitgraph { 157div#cgit table.list td.commitgraph {
157 font-family: monospace; 158 font-family: monospace;
158 white-space: pre; 159 white-space: pre;
159} 160}
160 161
161table.list td.commitgraph .column1 { 162div#cgit table.list td.commitgraph .column1 {
162 color: #a00; 163 color: #a00;
163} 164}
164 165
165table.list td.commitgraph .column2 { 166div#cgit table.list td.commitgraph .column2 {
166 color: #0a0; 167 color: #0a0;
167} 168}
168 169
169table.list td.commitgraph .column3 { 170div#cgit table.list td.commitgraph .column3 {
170 color: #aa0; 171 color: #aa0;
171} 172}
172 173
173table.list td.commitgraph .column4 { 174div#cgit table.list td.commitgraph .column4 {
174 color: #00a; 175 color: #00a;
175} 176}
176 177
177table.list td.commitgraph .column5 { 178div#cgit table.list td.commitgraph .column5 {
178 color: #a0a; 179 color: #a0a;
179} 180}
180 181
181table.list td.commitgraph .column6 { 182div#cgit table.list td.commitgraph .column6 {
182 color: #0aa; 183 color: #0aa;
183} 184}
184 185
185table.list td.logsubject { 186div#cgit table.list td.logsubject {
186 font-family: monospace; 187 font-family: monospace;
187 font-weight: bold; 188 font-weight: bold;
188} 189}
189 190
190table.list td.logmsg { 191div#cgit table.list td.logmsg {
191 font-family: monospace; 192 font-family: monospace;
192 white-space: pre; 193 white-space: pre;
193 padding: 0 0.5em; 194 padding: 0 0.5em;
194} 195}
195 196
196table.list td a { 197div#cgit table.list td a {
197 color: black; 198 color: black;
198} 199}
199 200
200table.list td a.ls-dir { 201div#cgit table.list td a.ls-dir {
201 font-weight: bold; 202 font-weight: bold;
202 color: #00f; 203 color: #00f;
203} 204}
204 205
205table.list td a:hover { 206div#cgit table.list td a:hover {
206 color: #00f; 207 color: #00f;
207} 208}
208 209
209img { 210div#cgit img {
210 border: none; 211 border: none;
211} 212}
212 213
213input#switch-btn { 214div#cgit input#switch-btn {
214 margin: 2px 0px 0px 0px; 215 margin: 2px 0px 0px 0px;
215} 216}
216 217
217td#sidebar input.txt { 218div#cgit td#sidebar input.txt {
218 width: 100%; 219 width: 100%;
219 margin: 2px 0px 0px 0px; 220 margin: 2px 0px 0px 0px;
220} 221}
221 222
222table#grid { 223div#cgit table#grid {
223 margin: 0px; 224 margin: 0px;
224} 225}
225 226
226td#content { 227div#cgit td#content {
227 vertical-align: top; 228 vertical-align: top;
228 padding: 1em 2em 1em 1em; 229 padding: 1em 2em 1em 1em;
229 border: none; 230 border: none;
230} 231}
231 232
232div#summary { 233div#cgit div#summary {
233 vertical-align: top; 234 vertical-align: top;
234 margin-bottom: 1em; 235 margin-bottom: 1em;
235} 236}
236 237
237table#downloads { 238div#cgit table#downloads {
238 float: right; 239 float: right;
239 border-collapse: collapse; 240 border-collapse: collapse;
240 border: solid 1px #777; 241 border: solid 1px #777;
@@ -242,125 +243,152 @@ table#downloads {
242 margin-bottom: 0.5em; 243 margin-bottom: 0.5em;
243} 244}
244 245
245table#downloads th { 246div#cgit table#downloads th {
246 background-color: #ccc; 247 background-color: #ccc;
247} 248}
248 249
249div#blob { 250div#cgit div#blob {
250 border: solid 1px black; 251 border: solid 1px black;
251} 252}
252 253
253div.error { 254div#cgit div.error {
254 color: red; 255 color: red;
255 font-weight: bold; 256 font-weight: bold;
256 margin: 1em 2em; 257 margin: 1em 2em;
257} 258}
258 259
259a.ls-blob, a.ls-dir, a.ls-mod { 260div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit a.ls-mod {
260 font-family: monospace; 261 font-family: monospace;
261} 262}
262 263
263td.ls-size { 264div#cgit td.ls-size {
264 text-align: right; 265 text-align: right;
265 font-family: monospace; 266 font-family: monospace;
266 width: 10em; 267 width: 10em;
267} 268}
268 269
269td.ls-mode { 270div#cgit td.ls-mode {
270 font-family: monospace; 271 font-family: monospace;
271 width: 10em; 272 width: 10em;
272} 273}
273 274
274table.blob { 275div#cgit table.blob {
275 margin-top: 0.5em; 276 margin-top: 0.5em;
276 border-top: solid 1px black; 277 border-top: solid 1px black;
277} 278}
278 279
279table.blob td.lines { 280div#cgit table.blob td.lines {
280 margin: 0; padding: 0 0 0 0.5em; 281 margin: 0; padding: 0 0 0 0.5em;
281 vertical-align: top; 282 vertical-align: top;
282 color: black; 283 color: black;
283} 284}
284 285
285table.blob td.linenumbers { 286div#cgit table.blob td.linenumbers {
286 margin: 0; padding: 0 0.5em 0 0.5em; 287 margin: 0; padding: 0 0.5em 0 0.5em;
287 vertical-align: top; 288 vertical-align: top;
288 text-align: right; 289 text-align: right;
289 border-right: 1px solid gray; 290 border-right: 1px solid gray;
290} 291}
291 292
292table.blob pre { 293div#cgit table.blob pre {
293 padding: 0; margin: 0; 294 padding: 0; margin: 0;
294} 295}
295 296
296table.blob a.no, table.ssdiff a.no { 297div#cgit table.blob a.no, div#cgit table.ssdiff a.no {
297 color: gray; 298 color: gray;
298 text-align: right; 299 text-align: right;
299 text-decoration: none; 300 text-decoration: none;
300} 301}
301 302
302table.blob a.no a:hover { 303div#cgit table.blob a.no a:hover {
303 color: black; 304 color: black;
304} 305}
305 306
306table.bin-blob { 307div#cgit table.bin-blob {
307 margin-top: 0.5em; 308 margin-top: 0.5em;
308 border: solid 1px black; 309 border: solid 1px black;
309} 310}
310 311
311table.bin-blob th { 312div#cgit table.bin-blob th {
312 font-family: monospace; 313 font-family: monospace;
313 white-space: pre; 314 white-space: pre;
314 border: solid 1px #777; 315 border: solid 1px #777;
315 padding: 0.5em 1em; 316 padding: 0.5em 1em;
316} 317}
317 318
318table.bin-blob td { 319div#cgit table.bin-blob td {
319 font-family: monospace; 320 font-family: monospace;
320 white-space: pre; 321 white-space: pre;
321 border-left: solid 1px #777; 322 border-left: solid 1px #777;
322 padding: 0em 1em; 323 padding: 0em 1em;
323} 324}
324 325
325table.nowrap td { 326div#cgit table.nowrap td {
326 white-space: nowrap; 327 white-space: nowrap;
327} 328}
328 329
329table.commit-info { 330div#cgit table.commit-info {
330 border-collapse: collapse; 331 border-collapse: collapse;
331 margin-top: 1.5em; 332 margin-top: 1.5em;
332} 333}
333 334
334table.commit-info th { 335div#cgit div.cgit-panel {
336 float: right;
337 margin-top: 1.5em;
338}
339
340div#cgit div.cgit-panel table {
341 border-collapse: collapse;
342 border: solid 1px #aaa;
343 background-color: #eee;
344}
345
346div#cgit div.cgit-panel th {
347 text-align: center;
348}
349
350div#cgit div.cgit-panel td {
351 padding: 0.25em 0.5em;
352}
353
354div#cgit div.cgit-panel td.label {
355 padding-right: 0.5em;
356}
357
358div#cgit div.cgit-panel td.ctrl {
359 padding-left: 0.5em;
360}
361
362div#cgit table.commit-info th {
335 text-align: left; 363 text-align: left;
336 font-weight: normal; 364 font-weight: normal;
337 padding: 0.1em 1em 0.1em 0.1em; 365 padding: 0.1em 1em 0.1em 0.1em;
338 vertical-align: top; 366 vertical-align: top;
339} 367}
340 368
341table.commit-info td { 369div#cgit table.commit-info td {
342 font-weight: normal; 370 font-weight: normal;
343 padding: 0.1em 1em 0.1em 0.1em; 371 padding: 0.1em 1em 0.1em 0.1em;
344} 372}
345 373
346div.commit-subject { 374div#cgit div.commit-subject {
347 font-weight: bold; 375 font-weight: bold;
348 font-size: 125%; 376 font-size: 125%;
349 margin: 1.5em 0em 0.5em 0em; 377 margin: 1.5em 0em 0.5em 0em;
350 padding: 0em; 378 padding: 0em;
351} 379}
352 380
353div.commit-msg { 381div#cgit div.commit-msg {
354 white-space: pre; 382 white-space: pre;
355 font-family: monospace; 383 font-family: monospace;
356} 384}
357 385
358div.notes-header { 386div#cgit div.notes-header {
359 font-weight: bold; 387 font-weight: bold;
360 padding-top: 1.5em; 388 padding-top: 1.5em;
361} 389}
362 390
363div.notes { 391div#cgit div.notes {
364 white-space: pre; 392 white-space: pre;
365 font-family: monospace; 393 font-family: monospace;
366 border: solid 1px #ee9; 394 border: solid 1px #ee9;
@@ -369,22 +397,22 @@ div.notes {
369 float: left; 397 float: left;
370} 398}
371 399
372div.notes-footer { 400div#cgit div.notes-footer {
373 clear: left; 401 clear: left;
374} 402}
375 403
376div.diffstat-header { 404div#cgit div.diffstat-header {
377 font-weight: bold; 405 font-weight: bold;
378 padding-top: 1.5em; 406 padding-top: 1.5em;
379} 407}
380 408
381table.diffstat { 409div#cgit table.diffstat {
382 border-collapse: collapse; 410 border-collapse: collapse;
383 border: solid 1px #aaa; 411 border: solid 1px #aaa;
384 background-color: #eee; 412 background-color: #eee;
385} 413}
386 414
387table.diffstat th { 415div#cgit table.diffstat th {
388 font-weight: normal; 416 font-weight: normal;
389 text-align: left; 417 text-align: left;
390 text-decoration: underline; 418 text-decoration: underline;
@@ -392,282 +420,286 @@ table.diffstat th {
392 font-size: 100%; 420 font-size: 100%;
393} 421}
394 422
395table.diffstat td { 423div#cgit table.diffstat td {
396 padding: 0.2em 0.2em 0.1em 0.1em; 424 padding: 0.2em 0.2em 0.1em 0.1em;
397 font-size: 100%; 425 font-size: 100%;
398 border: none; 426 border: none;
399} 427}
400 428
401table.diffstat td.mode { 429div#cgit table.diffstat td.mode {
402 white-space: nowrap; 430 white-space: nowrap;
403} 431}
404 432
405table.diffstat td span.modechange { 433div#cgit table.diffstat td span.modechange {
406 padding-left: 1em; 434 padding-left: 1em;
407 color: red; 435 color: red;
408} 436}
409 437
410table.diffstat td.add a { 438div#cgit table.diffstat td.add a {
411 color: green; 439 color: green;
412} 440}
413 441
414table.diffstat td.del a { 442div#cgit table.diffstat td.del a {
415 color: red; 443 color: red;
416} 444}
417 445
418table.diffstat td.upd a { 446div#cgit table.diffstat td.upd a {
419 color: blue; 447 color: blue;
420} 448}
421 449
422table.diffstat td.graph { 450div#cgit table.diffstat td.graph {
423 width: 500px; 451 width: 500px;
424 vertical-align: middle; 452 vertical-align: middle;
425} 453}
426 454
427table.diffstat td.graph table { 455div#cgit table.diffstat td.graph table {
428 border: none; 456 border: none;
429} 457}
430 458
431table.diffstat td.graph td { 459div#cgit table.diffstat td.graph td {
432 padding: 0px; 460 padding: 0px;
433 border: 0px; 461 border: 0px;
434 height: 7pt; 462 height: 7pt;
435} 463}
436 464
437table.diffstat td.graph td.add { 465div#cgit table.diffstat td.graph td.add {
438 background-color: #5c5; 466 background-color: #5c5;
439} 467}
440 468
441table.diffstat td.graph td.rem { 469div#cgit table.diffstat td.graph td.rem {
442 background-color: #c55; 470 background-color: #c55;
443} 471}
444 472
445div.diffstat-summary { 473div#cgit div.diffstat-summary {
446 color: #888; 474 color: #888;
447 padding-top: 0.5em; 475 padding-top: 0.5em;
448} 476}
449 477
450table.diff { 478div#cgit table.diff {
451 width: 100%; 479 width: 100%;
452} 480}
453 481
454table.diff td { 482div#cgit table.diff td {
455 font-family: monospace; 483 font-family: monospace;
456 white-space: pre; 484 white-space: pre;
457} 485}
458 486
459table.diff td div.head { 487div#cgit table.diff td div.head {
460 font-weight: bold; 488 font-weight: bold;
461 margin-top: 1em; 489 margin-top: 1em;
462 color: black; 490 color: black;
463} 491}
464 492
465table.diff td div.hunk { 493div#cgit table.diff td div.hunk {
466 color: #009; 494 color: #009;
467} 495}
468 496
469table.diff td div.add { 497div#cgit table.diff td div.add {
470 color: green; 498 color: green;
471} 499}
472 500
473table.diff td div.del { 501div#cgit table.diff td div.del {
474 color: red; 502 color: red;
475} 503}
476 504
477.sha1 { 505div#cgit .sha1 {
478 font-family: monospace; 506 font-family: monospace;
479 font-size: 90%; 507 font-size: 90%;
480} 508}
481 509
482.left { 510div#cgit .left {
483 text-align: left; 511 text-align: left;
484} 512}
485 513
486.right { 514div#cgit .right {
487 text-align: right; 515 text-align: right;
488} 516}
489 517
490table.list td.reposection { 518div#cgit table.list td.reposection {
491 font-style: italic; 519 font-style: italic;
492 color: #888; 520 color: #888;
493} 521}
494 522
495a.button { 523div#cgit a.button {
496 font-size: 80%; 524 font-size: 80%;
497 padding: 0em 0.5em; 525 padding: 0em 0.5em;
498} 526}
499 527
500a.primary { 528div#cgit a.primary {
501 font-size: 100%; 529 font-size: 100%;
502} 530}
503 531
504a.secondary { 532div#cgit a.secondary {
505 font-size: 90%; 533 font-size: 90%;
506} 534}
507 535
508td.toplevel-repo { 536div#cgit td.toplevel-repo {
509 537
510} 538}
511 539
512table.list td.sublevel-repo { 540div#cgit table.list td.sublevel-repo {
513 padding-left: 1.5em; 541 padding-left: 1.5em;
514} 542}
515 543
516div.pager { 544div#cgit div.pager {
517 text-align: center; 545 text-align: center;
518 margin: 1em 0em 0em 0em; 546 margin: 1em 0em 0em 0em;
519} 547}
520 548
521div.pager a { 549div#cgit div.pager a {
522 color: #777; 550 color: #777;
523 margin: 0em 0.5em; 551 margin: 0em 0.5em;
524} 552}
525 553
526span.age-mins { 554div#cgit span.age-mins {
527 font-weight: bold; 555 font-weight: bold;
528 color: #080; 556 color: #080;
529} 557}
530 558
531span.age-hours { 559div#cgit span.age-hours {
532 color: #080; 560 color: #080;
533} 561}
534 562
535span.age-days { 563div#cgit span.age-days {
536 color: #040; 564 color: #040;
537} 565}
538 566
539span.age-weeks { 567div#cgit span.age-weeks {
540 color: #444; 568 color: #444;
541} 569}
542 570
543span.age-months { 571div#cgit span.age-months {
544 color: #888; 572 color: #888;
545} 573}
546 574
547span.age-years { 575div#cgit span.age-years {
548 color: #bbb; 576 color: #bbb;
549} 577}
550div.footer { 578div#cgit div.footer {
551 margin-top: 0.5em; 579 margin-top: 0.5em;
552 text-align: center; 580 text-align: center;
553 font-size: 80%; 581 font-size: 80%;
554 color: #ccc; 582 color: #ccc;
555} 583}
556a.branch-deco { 584div#cgit a.branch-deco {
585 color: #000;
557 margin: 0px 0.5em; 586 margin: 0px 0.5em;
558 padding: 0px 0.25em; 587 padding: 0px 0.25em;
559 background-color: #88ff88; 588 background-color: #88ff88;
560 border: solid 1px #007700; 589 border: solid 1px #007700;
561} 590}
562a.tag-deco { 591div#cgit a.tag-deco {
592 color: #000;
563 margin: 0px 0.5em; 593 margin: 0px 0.5em;
564 padding: 0px 0.25em; 594 padding: 0px 0.25em;
565 background-color: #ffff88; 595 background-color: #ffff88;
566 border: solid 1px #777700; 596 border: solid 1px #777700;
567} 597}
568a.remote-deco { 598div#cgit a.remote-deco {
599 color: #000;
569 margin: 0px 0.5em; 600 margin: 0px 0.5em;
570 padding: 0px 0.25em; 601 padding: 0px 0.25em;
571 background-color: #ccccff; 602 background-color: #ccccff;
572 border: solid 1px #000077; 603 border: solid 1px #000077;
573} 604}
574a.deco { 605div#cgit a.deco {
606 color: #000;
575 margin: 0px 0.5em; 607 margin: 0px 0.5em;
576 padding: 0px 0.25em; 608 padding: 0px 0.25em;
577 background-color: #ff8888; 609 background-color: #ff8888;
578 border: solid 1px #770000; 610 border: solid 1px #770000;
579} 611}
580 612
581div.commit-subject a.branch-deco, 613div#cgit div.commit-subject a.branch-deco,
582div.commit-subject a.tag-deco, 614div#cgit div.commit-subject a.tag-deco,
583div.commit-subject a.remote-deco, 615div#cgit div.commit-subject a.remote-deco,
584div.commit-subject a.deco { 616div#cgit div.commit-subject a.deco {
585 margin-left: 1em; 617 margin-left: 1em;
586 font-size: 75%; 618 font-size: 75%;
587} 619}
588 620
589table.stats { 621div#cgit table.stats {
590 border: solid 1px black; 622 border: solid 1px black;
591 border-collapse: collapse; 623 border-collapse: collapse;
592} 624}
593 625
594table.stats th { 626div#cgit table.stats th {
595 text-align: left; 627 text-align: left;
596 padding: 1px 0.5em; 628 padding: 1px 0.5em;
597 background-color: #eee; 629 background-color: #eee;
598 border: solid 1px black; 630 border: solid 1px black;
599} 631}
600 632
601table.stats td { 633div#cgit table.stats td {
602 text-align: right; 634 text-align: right;
603 padding: 1px 0.5em; 635 padding: 1px 0.5em;
604 border: solid 1px black; 636 border: solid 1px black;
605} 637}
606 638
607table.stats td.total { 639div#cgit table.stats td.total {
608 font-weight: bold; 640 font-weight: bold;
609 text-align: left; 641 text-align: left;
610} 642}
611 643
612table.stats td.sum { 644div#cgit table.stats td.sum {
613 color: #c00; 645 color: #c00;
614 font-weight: bold; 646 font-weight: bold;
615/* background-color: #eee; */ 647/* background-color: #eee; */
616} 648}
617 649
618table.stats td.left { 650div#cgit table.stats td.left {
619 text-align: left; 651 text-align: left;
620} 652}
621 653
622table.vgraph { 654div#cgit table.vgraph {
623 border-collapse: separate; 655 border-collapse: separate;
624 border: solid 1px black; 656 border: solid 1px black;
625 height: 200px; 657 height: 200px;
626} 658}
627 659
628table.vgraph th { 660div#cgit table.vgraph th {
629 background-color: #eee; 661 background-color: #eee;
630 font-weight: bold; 662 font-weight: bold;
631 border: solid 1px white; 663 border: solid 1px white;
632 padding: 1px 0.5em; 664 padding: 1px 0.5em;
633} 665}
634 666
635table.vgraph td { 667div#cgit table.vgraph td {
636 vertical-align: bottom; 668 vertical-align: bottom;
637 padding: 0px 10px; 669 padding: 0px 10px;
638} 670}
639 671
640table.vgraph div.bar { 672div#cgit table.vgraph div.bar {
641 background-color: #eee; 673 background-color: #eee;
642} 674}
643 675
644table.hgraph { 676div#cgit table.hgraph {
645 border: solid 1px black; 677 border: solid 1px black;
646 width: 800px; 678 width: 800px;
647} 679}
648 680
649table.hgraph th { 681div#cgit table.hgraph th {
650 background-color: #eee; 682 background-color: #eee;
651 font-weight: bold; 683 font-weight: bold;
652 border: solid 1px black; 684 border: solid 1px black;
653 padding: 1px 0.5em; 685 padding: 1px 0.5em;
654} 686}
655 687
656table.hgraph td { 688div#cgit table.hgraph td {
657 vertical-align: middle; 689 vertical-align: middle;
658 padding: 2px 2px; 690 padding: 2px 2px;
659} 691}
660 692
661table.hgraph div.bar { 693div#cgit table.hgraph div.bar {
662 background-color: #eee; 694 background-color: #eee;
663 height: 1em; 695 height: 1em;
664} 696}
665 697
666table.ssdiff { 698div#cgit table.ssdiff {
667 width: 100%; 699 width: 100%;
668} 700}
669 701
670table.ssdiff td { 702div#cgit table.ssdiff td {
671 font-size: 75%; 703 font-size: 75%;
672 font-family: monospace; 704 font-family: monospace;
673 white-space: pre; 705 white-space: pre;
@@ -676,53 +708,53 @@ table.ssdiff td {
676 border-right: solid 1px #aaa; 708 border-right: solid 1px #aaa;
677} 709}
678 710
679table.ssdiff td.add { 711div#cgit table.ssdiff td.add {
680 color: black; 712 color: black;
681 background: #cfc; 713 background: #cfc;
682 min-width: 50%; 714 min-width: 50%;
683} 715}
684 716
685table.ssdiff td.add_dark { 717div#cgit table.ssdiff td.add_dark {
686 color: black; 718 color: black;
687 background: #aca; 719 background: #aca;
688 min-width: 50%; 720 min-width: 50%;
689} 721}
690 722
691table.ssdiff span.add { 723div#cgit table.ssdiff span.add {
692 background: #cfc; 724 background: #cfc;
693 font-weight: bold; 725 font-weight: bold;
694} 726}
695 727
696table.ssdiff td.del { 728div#cgit table.ssdiff td.del {
697 color: black; 729 color: black;
698 background: #fcc; 730 background: #fcc;
699 min-width: 50%; 731 min-width: 50%;
700} 732}
701 733
702table.ssdiff td.del_dark { 734div#cgit table.ssdiff td.del_dark {
703 color: black; 735 color: black;
704 background: #caa; 736 background: #caa;
705 min-width: 50%; 737 min-width: 50%;
706} 738}
707 739
708table.ssdiff span.del { 740div#cgit table.ssdiff span.del {
709 background: #fcc; 741 background: #fcc;
710 font-weight: bold; 742 font-weight: bold;
711} 743}
712 744
713table.ssdiff td.changed { 745div#cgit table.ssdiff td.changed {
714 color: black; 746 color: black;
715 background: #ffc; 747 background: #ffc;
716 min-width: 50%; 748 min-width: 50%;
717} 749}
718 750
719table.ssdiff td.changed_dark { 751div#cgit table.ssdiff td.changed_dark {
720 color: black; 752 color: black;
721 background: #cca; 753 background: #cca;
722 min-width: 50%; 754 min-width: 50%;
723} 755}
724 756
725table.ssdiff td.lineno { 757div#cgit table.ssdiff td.lineno {
726 color: black; 758 color: black;
727 background: #eee; 759 background: #eee;
728 text-align: right; 760 text-align: right;
@@ -730,48 +762,48 @@ table.ssdiff td.lineno {
730 min-width: 3em; 762 min-width: 3em;
731} 763}
732 764
733table.ssdiff td.hunk { 765div#cgit table.ssdiff td.hunk {
734 color: black; 766 color: black;
735 background: #ccf; 767 background: #ccf;
736 border-top: solid 1px #aaa; 768 border-top: solid 1px #aaa;
737 border-bottom: solid 1px #aaa; 769 border-bottom: solid 1px #aaa;
738} 770}
739 771
740table.ssdiff td.head { 772div#cgit table.ssdiff td.head {
741 border-top: solid 1px #aaa; 773 border-top: solid 1px #aaa;
742 border-bottom: solid 1px #aaa; 774 border-bottom: solid 1px #aaa;
743} 775}
744 776
745table.ssdiff td.head div.head { 777div#cgit table.ssdiff td.head div.head {
746 font-weight: bold; 778 font-weight: bold;
747 color: black; 779 color: black;
748} 780}
749 781
750table.ssdiff td.foot { 782div#cgit table.ssdiff td.foot {
751 border-top: solid 1px #aaa; 783 border-top: solid 1px #aaa;
752 border-left: none; 784 border-left: none;
753 border-right: none; 785 border-right: none;
754 border-bottom: none; 786 border-bottom: none;
755} 787}
756 788
757table.ssdiff td.space { 789div#cgit table.ssdiff td.space {
758 border: none; 790 border: none;
759} 791}
760 792
761table.ssdiff td.space div { 793div#cgit table.ssdiff td.space div {
762 min-height: 3em; 794 min-height: 3em;
763} 795}
764 796
765/* Syntax highlighting */ 797/* Syntax highlighting */
766table.blob .num { color:#2928ff; } 798div#cgit table.blob .num { color:#2928ff; }
767table.blob .esc { color:#ff00ff; } 799div#cgit table.blob .esc { color:#ff00ff; }
768table.blob .str { color:#ff0000; } 800div#cgit table.blob .str { color:#ff0000; }
769table.blob .dstr { color:#818100; } 801div#cgit table.blob .dstr { color:#818100; }
770table.blob .slc { color:#838183; font-style:italic; } 802div#cgit table.blob .slc { color:#838183; font-style:italic; }
771table.blob .com { color:#838183; font-style:italic; } 803div#cgit table.blob .com { color:#838183; font-style:italic; }
772table.blob .dir { color:#008200; } 804div#cgit table.blob .dir { color:#008200; }
773table.blob .sym { color:#000000; } 805div#cgit table.blob .sym { color:#000000; }
774table.blob .kwa { color:#000000; font-weight:bold; } 806div#cgit table.blob .kwa { color:#000000; font-weight:bold; }
775table.blob .kwb { color:#830000; } 807div#cgit table.blob .kwb { color:#830000; }
776table.blob .kwc { color:#000000; font-weight:bold; } 808div#cgit table.blob .kwc { color:#000000; font-weight:bold; }
777table.blob .kwd { color:#010181; } 809div#cgit table.blob .kwd { color:#010181; }
diff --git a/cgit.h b/cgit.h
index 63847ff..c7ab5c6 100644
--- a/cgit.h
+++ b/cgit.h
@@ -51,6 +51,10 @@ typedef void (*configfn)(const char *name, const char *value);
51typedef void (*filepair_fn)(struct diff_filepair *pair); 51typedef void (*filepair_fn)(struct diff_filepair *pair);
52typedef void (*linediff_fn)(char *line, int len); 52typedef void (*linediff_fn)(char *line, int len);
53 53
54typedef enum {
55 ABOUT, COMMIT, SOURCE
56} filter_type;
57
54struct cgit_filter { 58struct cgit_filter {
55 char *cmd; 59 char *cmd;
56 char **argv; 60 char **argv;
@@ -162,6 +166,7 @@ struct cgit_config {
162 char *agefile; 166 char *agefile;
163 char *cache_root; 167 char *cache_root;
164 char *clone_prefix; 168 char *clone_prefix;
169 char *clone_url;
165 char *css; 170 char *css;
166 char *favicon; 171 char *favicon;
167 char *footer; 172 char *footer;
@@ -192,6 +197,7 @@ struct cgit_config {
192 int embedded; 197 int embedded;
193 int enable_filter_overrides; 198 int enable_filter_overrides;
194 int enable_gitweb_owner; 199 int enable_gitweb_owner;
200 int enable_http_clone;
195 int enable_index_links; 201 int enable_index_links;
196 int enable_commit_graph; 202 int enable_commit_graph;
197 int enable_log_filecount; 203 int enable_log_filecount;
@@ -318,6 +324,8 @@ extern int cgit_parse_snapshots_mask(const char *str);
318extern int cgit_open_filter(struct cgit_filter *filter); 324extern int cgit_open_filter(struct cgit_filter *filter);
319extern int cgit_close_filter(struct cgit_filter *filter); 325extern int cgit_close_filter(struct cgit_filter *filter);
320 326
327extern void cgit_prepare_repo_env(struct cgit_repo * repo);
328
321extern int readfile(const char *path, char **buf, size_t *size); 329extern int readfile(const char *path, char **buf, size_t *size);
322 330
323extern char *expand_macros(const char *txt); 331extern char *expand_macros(const char *txt);
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 26c4bfc..a22423b 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -31,7 +31,7 @@ about-filter::
31 about pages (both top-level and for each repository). The command will 31 about pages (both top-level and for each repository). The command will
32 get the content of the about-file on its STDIN, and the STDOUT from the 32 get the content of the about-file on its STDIN, and the STDOUT from the
33 command will be included verbatim on the about page. Default value: 33 command will be included verbatim on the about page. Default value:
34 none. 34 none. See also: "FILTER API".
35 35
36agefile:: 36agefile::
37 Specifies a path, relative to each repository path, which can be used 37 Specifies a path, relative to each repository path, which can be used
@@ -76,11 +76,17 @@ clone-prefix::
76 setting is only used if `repo.clone-url` is unspecified. Default value: 76 setting is only used if `repo.clone-url` is unspecified. Default value:
77 none. 77 none.
78 78
79clone-url::
80 Space-separated list of clone-url templates. This setting is only
81 used if `repo.clone-url` is unspecified. Default value: none. See
82 also: "MACRO EXPANSION", "FILTER API".
83
79commit-filter:: 84commit-filter::
80 Specifies a command which will be invoked to format commit messages. 85 Specifies a command which will be invoked to format commit messages.
81 The command will get the message on its STDIN, and the STDOUT from the 86 The command will get the message on its STDIN, and the STDOUT from the
82 command will be included verbatim as the commit message, i.e. this can 87 command will be included verbatim as the commit message, i.e. this can
83 be used to implement bugtracker integration. Default value: none. 88 be used to implement bugtracker integration. Default value: none.
89 See also: "FILTER API".
84 90
85css:: 91css::
86 Url which specifies the css document to include in all cgit pages. 92 Url which specifies the css document to include in all cgit pages.
@@ -105,6 +111,11 @@ enable-gitweb-owner::
105 for the git config value "gitweb.owner" to determine the owner. 111 for the git config value "gitweb.owner" to determine the owner.
106 Default value: "1". See also: scan-path. 112 Default value: "1". See also: scan-path.
107 113
114enable-http-clone::
115 If set to "1", cgit will act as an dumb HTTP endpoint for git clones.
116 If you use an alternate way of serving git repositories, you may wish
117 to disable this. Default value: "1".
118
108enable-index-links:: 119enable-index-links::
109 Flag which, when set to "1", will make cgit generate extra links for 120 Flag which, when set to "1", will make cgit generate extra links for
110 each repo in the repository index (specifically, to the "summary", 121 each repo in the repository index (specifically, to the "summary",
@@ -320,7 +331,7 @@ source-filter::
320 and the name of the blob as its only command line argument. The STDOUT 331 and the name of the blob as its only command line argument. The STDOUT
321 from the command will be included verbatim as the blob contents, i.e. 332 from the command will be included verbatim as the blob contents, i.e.
322 this can be used to implement e.g. syntax highlighting. Default value: 333 this can be used to implement e.g. syntax highlighting. Default value:
323 none. 334 none. See also: "FILTER API".
324 335
325summary-branches:: 336summary-branches::
326 Specifies the number of branches to display in the repository "summary" 337 Specifies the number of branches to display in the repository "summary"
@@ -353,15 +364,15 @@ REPOSITORY SETTINGS
353------------------- 364-------------------
354repo.about-filter:: 365repo.about-filter::
355 Override the default about-filter. Default value: none. See also: 366 Override the default about-filter. Default value: none. See also:
356 "enable-filter-overrides". 367 "enable-filter-overrides". See also: "FILTER API".
357 368
358repo.clone-url:: 369repo.clone-url::
359 A list of space-separated urls which can be used to clone this repo. 370 A list of space-separated urls which can be used to clone this repo.
360 Default value: none. 371 Default value: none. See also: "MACRO EXPANSION".
361 372
362repo.commit-filter:: 373repo.commit-filter::
363 Override the default commit-filter. Default value: none. See also: 374 Override the default commit-filter. Default value: none. See also:
364 "enable-filter-overrides". 375 "enable-filter-overrides". See also: "FILTER API".
365 376
366repo.defbranch:: 377repo.defbranch::
367 The name of the default branch for this repository. If no such branch 378 The name of the default branch for this repository. If no such branch
@@ -438,7 +449,7 @@ repo.section::
438 449
439repo.source-filter:: 450repo.source-filter::
440 Override the default source-filter. Default value: none. See also: 451 Override the default source-filter. Default value: none. See also:
441 "enable-filter-overrides". 452 "enable-filter-overrides". See also: "FILTER API".
442 453
443repo.url:: 454repo.url::
444 The relative url used to access the repository. This must be the first 455 The relative url used to access the repository. This must be the first
@@ -458,6 +469,40 @@ Note: the "repo." prefix is dropped from the option names in repo-specific
458config files, e.g. "repo.desc" becomes "desc". 469config files, e.g. "repo.desc" becomes "desc".
459 470
460 471
472FILTER API
473----------
474about filter::
475 This filter is given no arguments. The about text that is to be
476 filtered is available on standard input and the filtered text is
477 expected on standard output.
478
479commit filter::
480 This filter is given no arguments. The commit message text that is to
481 be filtered is available on standard input and the filtered text is
482 expected on standard output.
483
484source filter::
485 This filter is given a single parameter: the filename of the source
486 file to filter. The filter can use the filename to determine (for
487 example) the syntax highlighting mode. The contents of the source
488 file that is to be filtered is available on standard input and the
489 filtered contents is expected on standard output.
490
491Also, all filters are handed the following environment variables:
492
493- CGIT_REPO_URL (from repo.url)
494- CGIT_REPO_NAME (from repo.name)
495- CGIT_REPO_PATH (from repo.path)
496- CGIT_REPO_OWNER (from repo.owner)
497- CGIT_REPO_DEFBRANCH (from repo.defbranch)
498- CGIT_REPO_SECTION (from repo.section)
499- CGIT_REPO_CLONE_URL (from repo.clone-url)
500
501If a setting is not defined for a repository and the corresponding global
502setting is also not defined (if applicable), then the corresponding
503environment variable will be unset.
504
505
461MACRO EXPANSION 506MACRO EXPANSION
462--------------- 507---------------
463The following cgitrc options supports a simple macro expansion feature, 508The following cgitrc options supports a simple macro expansion feature,
@@ -477,6 +522,12 @@ can be accomplished by adding the following line to /etc/cgitrc:
477 522
478 include=/etc/cgitrc.d/$HTTP_HOST 523 include=/etc/cgitrc.d/$HTTP_HOST
479 524
525The following options are expanded during request processing, and support
526the environment variables defined in "FILTER API":
527
528- clone-url
529- repo.clone-url
530
480 531
481EXAMPLE CGITRC FILE 532EXAMPLE CGITRC FILE
482------------------- 533-------------------
@@ -486,8 +537,8 @@ EXAMPLE CGITRC FILE
486cache-size=1000 537cache-size=1000
487 538
488 539
489# Specify some default clone prefixes 540# Specify some default clone urls using macro expansion
490clone-prefix=git://example.com ssh://example.com/pub/git http://example.com/git 541clone-url=git://foo.org/$CGIT_REPO_URL git@foo.org:$CGIT_REPO_URL
491 542
492# Specify the css url 543# Specify the css url
493css=/css/cgit.css 544css=/css/cgit.css
diff --git a/cmd.c b/cmd.c
index 536515b..5a3d157 100644
--- a/cmd.c
+++ b/cmd.c
@@ -56,7 +56,7 @@ static void commit_fn(struct cgit_context *ctx)
56 56
57static void diff_fn(struct cgit_context *ctx) 57static void diff_fn(struct cgit_context *ctx)
58{ 58{
59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); 59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path, 1);
60} 60}
61 61
62static void info_fn(struct cgit_context *ctx) 62static void info_fn(struct cgit_context *ctx)
@@ -130,31 +130,31 @@ static void tree_fn(struct cgit_context *ctx)
130 cgit_print_tree(ctx->qry.sha1, ctx->qry.path); 130 cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
131} 131}
132 132
133#define def_cmd(name, want_repo, want_layout, want_vpath) \ 133#define def_cmd(name, want_repo, want_layout, want_vpath, is_clone) \
134 {#name, name##_fn, want_repo, want_layout, want_vpath} 134 {#name, name##_fn, want_repo, want_layout, want_vpath, is_clone}
135 135
136struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx) 136struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
137{ 137{
138 static struct cgit_cmd cmds[] = { 138 static struct cgit_cmd cmds[] = {
139 def_cmd(HEAD, 1, 0, 0), 139 def_cmd(HEAD, 1, 0, 0, 1),
140 def_cmd(atom, 1, 0, 0), 140 def_cmd(atom, 1, 0, 0, 0),
141 def_cmd(about, 0, 1, 0), 141 def_cmd(about, 0, 1, 0, 0),
142 def_cmd(blob, 1, 0, 0), 142 def_cmd(blob, 1, 0, 0, 0),
143 def_cmd(commit, 1, 1, 1), 143 def_cmd(commit, 1, 1, 1, 0),
144 def_cmd(diff, 1, 1, 1), 144 def_cmd(diff, 1, 1, 1, 0),
145 def_cmd(info, 1, 0, 0), 145 def_cmd(info, 1, 0, 0, 1),
146 def_cmd(log, 1, 1, 1), 146 def_cmd(log, 1, 1, 1, 0),
147 def_cmd(ls_cache, 0, 0, 0), 147 def_cmd(ls_cache, 0, 0, 0, 0),
148 def_cmd(objects, 1, 0, 0), 148 def_cmd(objects, 1, 0, 0, 1),
149 def_cmd(patch, 1, 0, 1), 149 def_cmd(patch, 1, 0, 1, 0),
150 def_cmd(plain, 1, 0, 0), 150 def_cmd(plain, 1, 0, 0, 0),
151 def_cmd(refs, 1, 1, 0), 151 def_cmd(refs, 1, 1, 0, 0),
152 def_cmd(repolist, 0, 0, 0), 152 def_cmd(repolist, 0, 0, 0, 0),
153 def_cmd(snapshot, 1, 0, 0), 153 def_cmd(snapshot, 1, 0, 0, 0),
154 def_cmd(stats, 1, 1, 1), 154 def_cmd(stats, 1, 1, 1, 0),
155 def_cmd(summary, 1, 1, 0), 155 def_cmd(summary, 1, 1, 0, 0),
156 def_cmd(tag, 1, 1, 0), 156 def_cmd(tag, 1, 1, 0, 0),
157 def_cmd(tree, 1, 1, 1), 157 def_cmd(tree, 1, 1, 1, 0),
158 }; 158 };
159 int i; 159 int i;
160 160
diff --git a/cmd.h b/cmd.h
index 8dc01bd..eb5bc87 100644
--- a/cmd.h
+++ b/cmd.h
@@ -8,7 +8,8 @@ struct cgit_cmd {
8 cgit_cmd_fn fn; 8 cgit_cmd_fn fn;
9 unsigned int want_repo:1, 9 unsigned int want_repo:1,
10 want_layout:1, 10 want_layout:1,
11 want_vpath:1; 11 want_vpath:1,
12 is_clone:1;
12}; 13};
13 14
14extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx); 15extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx);
diff --git a/filters/commit-links.sh b/filters/commit-links.sh
index 110c609..5881952 100755
--- a/filters/commit-links.sh
+++ b/filters/commit-links.sh
@@ -3,12 +3,26 @@
3# 3#
4# To use this script, refer to this file with either the commit-filter or the 4# To use this script, refer to this file with either the commit-filter or the
5# repo.commit-filter options in cgitrc. 5# repo.commit-filter options in cgitrc.
6#
7# The following environment variables can be used to retrieve the configuration
8# of the repository for which this script is called:
9# CGIT_REPO_URL ( = repo.url setting )
10# CGIT_REPO_NAME ( = repo.name setting )
11# CGIT_REPO_PATH ( = repo.path setting )
12# CGIT_REPO_OWNER ( = repo.owner setting )
13# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
14# CGIT_REPO_SECTION ( = section setting )
15# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
16#
17
18regex=''
6 19
7# This expression generates links to commits referenced by their SHA1. 20# This expression generates links to commits referenced by their SHA1.
8regex=$regex' 21regex=$regex'
9s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g' 22s|\b([0-9a-fA-F]{7,40})\b|<a href="./?id=\1">\1</a>|g'
23
10# This expression generates links to a fictional bugtracker. 24# This expression generates links to a fictional bugtracker.
11regex=$regex' 25regex=$regex'
12s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g' 26s|#([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g'
13 27
14sed -re "$regex" 28sed -re "$regex"
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
index 0acdf12..5fcc9c9 100755
--- a/filters/syntax-highlighting.sh
+++ b/filters/syntax-highlighting.sh
@@ -23,6 +23,17 @@
23# table.blob .kwb { color:#830000; } 23# table.blob .kwb { color:#830000; }
24# table.blob .kwc { color:#000000; font-weight:bold; } 24# table.blob .kwc { color:#000000; font-weight:bold; }
25# table.blob .kwd { color:#010181; } 25# table.blob .kwd { color:#010181; }
26#
27# The following environment variables can be used to retrieve the configuration
28# of the repository for which this script is called:
29# CGIT_REPO_URL ( = repo.url setting )
30# CGIT_REPO_NAME ( = repo.name setting )
31# CGIT_REPO_PATH ( = repo.path setting )
32# CGIT_REPO_OWNER ( = repo.owner setting )
33# CGIT_REPO_DEFBRANCH ( = repo.defbranch setting )
34# CGIT_REPO_SECTION ( = section setting )
35# CGIT_REPO_CLONE_URL ( = repo.clone-url setting )
36#
26 37
27# store filename and extension in local vars 38# store filename and extension in local vars
28BASENAME="$1" 39BASENAME="$1"
diff --git a/html.c b/html.c
index 5b07aa0..8f6e4f6 100644
--- a/html.c
+++ b/html.c
@@ -215,6 +215,14 @@ void html_option(const char *value, const char *text, const char *selected_value
215 html("</option>\n"); 215 html("</option>\n");
216} 216}
217 217
218void html_intoption(int value, const char *text, int selected_value)
219{
220 htmlf("<option value='%d'%s>", value,
221 value == selected_value ? " selected='selected'" : "");
222 html_txt(text);
223 html("</option>");
224}
225
218void html_link_open(const char *url, const char *title, const char *class) 226void html_link_open(const char *url, const char *title, const char *class)
219{ 227{
220 html("<a href='"); 228 html("<a href='");
diff --git a/html.h b/html.h
index 1135fb8..20e1dc2 100644
--- a/html.h
+++ b/html.h
@@ -17,6 +17,7 @@ extern void html_url_path(const char *txt);
17extern void html_url_arg(const char *txt); 17extern void html_url_arg(const char *txt);
18extern void html_hidden(const char *name, const char *value); 18extern void html_hidden(const char *name, const char *value);
19extern void html_option(const char *value, const char *text, const char *selected_value); 19extern void html_option(const char *value, const char *text, const char *selected_value);
20extern void html_intoption(int value, const char *text, int selected_value);
20extern void html_link_open(const char *url, const char *title, const char *class); 21extern void html_link_open(const char *url, const char *title, const char *class);
21extern void html_link_close(void); 22extern void html_link_close(void);
22extern void html_fileperm(unsigned short mode); 23extern void html_fileperm(unsigned short mode);
diff --git a/shared.c b/shared.c
index 3778a5b..0c8ce3e 100644
--- a/shared.c
+++ b/shared.c
@@ -7,6 +7,7 @@
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include <stdio.h>
10 11
11struct cgit_repolist cgit_repolist; 12struct cgit_repolist cgit_repolist;
12struct cgit_context ctx; 13struct cgit_context ctx;
@@ -68,6 +69,7 @@ struct cgit_repo *cgit_add_repo(const char *url)
68 ret->about_filter = ctx.cfg.about_filter; 69 ret->about_filter = ctx.cfg.about_filter;
69 ret->commit_filter = ctx.cfg.commit_filter; 70 ret->commit_filter = ctx.cfg.commit_filter;
70 ret->source_filter = ctx.cfg.source_filter; 71 ret->source_filter = ctx.cfg.source_filter;
72 ret->clone_url = ctx.cfg.clone_url;
71 return ret; 73 return ret;
72} 74}
73 75
@@ -367,6 +369,33 @@ int cgit_parse_snapshots_mask(const char *str)
367 return rv; 369 return rv;
368} 370}
369 371
372typedef struct {
373 char * name;
374 char * value;
375} cgit_env_var;
376
377void cgit_prepare_repo_env(struct cgit_repo * repo)
378{
379 cgit_env_var env_vars[] = {
380 { .name = "CGIT_REPO_URL", .value = repo->url },
381 { .name = "CGIT_REPO_NAME", .value = repo->name },
382 { .name = "CGIT_REPO_PATH", .value = repo->path },
383 { .name = "CGIT_REPO_OWNER", .value = repo->owner },
384 { .name = "CGIT_REPO_DEFBRANCH", .value = repo->defbranch },
385 { .name = "CGIT_REPO_SECTION", .value = repo->section },
386 { .name = "CGIT_REPO_CLONE_URL", .value = repo->clone_url }
387 };
388 int env_var_count = ARRAY_SIZE(env_vars);
389 cgit_env_var *p, *q;
390 static char *warn = "cgit warning: failed to set env: %s=%s\n";
391
392 p = env_vars;
393 q = p + env_var_count;
394 for (; p < q; p++)
395 if (p->value && setenv(p->name, p->value, 1))
396 fprintf(stderr, warn, p->name, p->value);
397}
398
370int cgit_open_filter(struct cgit_filter *filter) 399int cgit_open_filter(struct cgit_filter *filter)
371{ 400{
372 401
diff --git a/tests/setup.sh b/tests/setup.sh
index 651f940..e3c6c17 100755
--- a/tests/setup.sh
+++ b/tests/setup.sh
@@ -63,6 +63,7 @@ enable-log-linecount=1
63summary-log=5 63summary-log=5
64summary-branches=5 64summary-branches=5
65summary-tags=5 65summary-tags=5
66clone-url=git://example.org/\$CGIT_REPO_URL.git
66 67
67repo.url=foo 68repo.url=foo
68repo.path=$PWD/trash/repos/foo/.git 69repo.path=$PWD/trash/repos/foo/.git
diff --git a/tests/t0102-summary.sh b/tests/t0102-summary.sh
index f0b0d9a..f299c5a 100755
--- a/tests/t0102-summary.sh
+++ b/tests/t0102-summary.sh
@@ -9,6 +9,9 @@ run_test 'find commit 1' 'grep -e "commit 1" trash/tmp'
9run_test 'find commit 5' 'grep -e "commit 5" trash/tmp' 9run_test 'find commit 5' 'grep -e "commit 5" trash/tmp'
10run_test 'find branch master' 'grep -e "master" trash/tmp' 10run_test 'find branch master' 'grep -e "master" trash/tmp'
11run_test 'no tags' '! grep -e "tags" trash/tmp' 11run_test 'no tags' '! grep -e "tags" trash/tmp'
12run_test 'clone-url expanded correctly' '
13 grep -e "git://example.org/foo.git" trash/tmp
14'
12 15
13run_test 'generate bar summary' 'cgit_url "bar" >trash/tmp' 16run_test 'generate bar summary' 'cgit_url "bar" >trash/tmp'
14run_test 'no commit 45' '! grep -e "commit 45" trash/tmp' 17run_test 'no commit 45' '! grep -e "commit 45" trash/tmp'
@@ -16,5 +19,8 @@ run_test 'find commit 46' 'grep -e "commit 46" trash/tmp'
16run_test 'find commit 50' 'grep -e "commit 50" trash/tmp' 19run_test 'find commit 50' 'grep -e "commit 50" trash/tmp'
17run_test 'find branch master' 'grep -e "master" trash/tmp' 20run_test 'find branch master' 'grep -e "master" trash/tmp'
18run_test 'no tags' '! grep -e "tags" trash/tmp' 21run_test 'no tags' '! grep -e "tags" trash/tmp'
22run_test 'clone-url expanded correctly' '
23 grep -e "git://example.org/bar.git" trash/tmp
24'
19 25
20tests_done 26tests_done
diff --git a/ui-commit.c b/ui-commit.c
index 2b4f677..536a8e8 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -39,7 +39,8 @@ void cgit_print_commit(char *hex, const char *prefix)
39 format_note(NULL, sha1, &notes, PAGE_ENCODING, 0); 39 format_note(NULL, sha1, &notes, PAGE_ENCODING, 0);
40 40
41 load_ref_decorations(DECORATE_FULL_REFS); 41 load_ref_decorations(DECORATE_FULL_REFS);
42 42
43 cgit_print_diff_ctrls();
43 html("<table summary='commit info' class='commit-info'>\n"); 44 html("<table summary='commit info' class='commit-info'>\n");
44 html("<tr><th>author</th><td>"); 45 html("<tr><th>author</th><td>");
45 html_txt(info->author); 46 html_txt(info->author);
@@ -64,11 +65,6 @@ void cgit_print_commit(char *hex, const char *prefix)
64 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix, 0); 65 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix, 0);
65 html(" ("); 66 html(" (");
66 cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix); 67 cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix);
67 html(") (");
68 if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff))
69 cgit_commit_link("unidiff", NULL, NULL, ctx.qry.head, tmp, prefix, 1);
70 else
71 cgit_commit_link("side-by-side diff", NULL, NULL, ctx.qry.head, tmp, prefix, 1);
72 html(")</td></tr>\n"); 68 html(")</td></tr>\n");
73 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 69 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
74 tmp = xstrdup(hex); 70 tmp = xstrdup(hex);
@@ -139,7 +135,7 @@ void cgit_print_commit(char *hex, const char *prefix)
139 tmp = sha1_to_hex(commit->parents->item->object.sha1); 135 tmp = sha1_to_hex(commit->parents->item->object.sha1);
140 else 136 else
141 tmp = NULL; 137 tmp = NULL;
142 cgit_print_diff(ctx.qry.sha1, tmp, prefix); 138 cgit_print_diff(ctx.qry.sha1, tmp, prefix, 0);
143 } 139 }
144 strbuf_release(&notes); 140 strbuf_release(&notes);
145 cgit_free_commitinfo(info); 141 cgit_free_commitinfo(info);
diff --git a/ui-diff.c b/ui-diff.c
index f8782df..c6bad63 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -169,7 +169,7 @@ static void inspect_filepair(struct diff_filepair *pair)
169void cgit_print_diffstat(const unsigned char *old_sha1, 169void cgit_print_diffstat(const unsigned char *old_sha1,
170 const unsigned char *new_sha1, const char *prefix) 170 const unsigned char *new_sha1, const char *prefix)
171{ 171{
172 int i, save_context = ctx.qry.context; 172 int i;
173 173
174 html("<div class='diffstat-header'>"); 174 html("<div class='diffstat-header'>");
175 cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, 175 cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
@@ -179,19 +179,6 @@ void cgit_print_diffstat(const unsigned char *old_sha1,
179 html_txt(prefix); 179 html_txt(prefix);
180 html("')"); 180 html("')");
181 } 181 }
182 html(" (");
183 ctx.qry.context = (save_context > 0 ? save_context : 3) << 1;
184 cgit_self_link("more", NULL, NULL, &ctx);
185 html("/");
186 ctx.qry.context = (save_context > 3 ? save_context : 3) >> 1;
187 cgit_self_link("less", NULL, NULL, &ctx);
188 ctx.qry.context = save_context;
189 html(" context)");
190 html(" (");
191 ctx.qry.ignorews = (ctx.qry.ignorews + 1) % 2;
192 cgit_self_link(ctx.qry.ignorews ? "ignore" : "show", NULL, NULL, &ctx);
193 ctx.qry.ignorews = (ctx.qry.ignorews + 1) % 2;
194 html(" whitespace changes)");
195 html("</div>"); 182 html("</div>");
196 html("<table summary='diffstat' class='diffstat'>"); 183 html("<table summary='diffstat' class='diffstat'>");
197 max_changes = 0; 184 max_changes = 0;
@@ -280,19 +267,6 @@ static void header(unsigned char *sha1, char *path1, int mode1,
280 html("</div>"); 267 html("</div>");
281} 268}
282 269
283static void print_ssdiff_link()
284{
285 if (!strcmp(ctx.qry.page, "diff")) {
286 if (use_ssdiff)
287 cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head,
288 ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1);
289 else
290 cgit_diff_link("Side-by-side diff", NULL, NULL,
291 ctx.qry.head, ctx.qry.sha1,
292 ctx.qry.sha2, ctx.qry.path, 1);
293 }
294}
295
296static void filepair_cb(struct diff_filepair *pair) 270static void filepair_cb(struct diff_filepair *pair)
297{ 271{
298 unsigned long old_size = 0; 272 unsigned long old_size = 0;
@@ -332,7 +306,54 @@ static void filepair_cb(struct diff_filepair *pair)
332 cgit_ssdiff_footer(); 306 cgit_ssdiff_footer();
333} 307}
334 308
335void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) 309void cgit_print_diff_ctrls()
310{
311 int i, curr;
312
313 html("<div class='cgit-panel'>");
314 html("<b>diff options</b>");
315 html("<form method='get' action='.'>");
316 cgit_add_hidden_formfields(1, 0, ctx.qry.page);
317 html("<table>");
318 html("<tr><td colspan='2'/></tr>");
319 html("<tr>");
320 html("<td class='label'>context:</td>");
321 html("<td class='ctrl'>");
322 html("<select name='context' onchange='this.form.submit();'>");
323 curr = ctx.qry.context;
324 if (!curr)
325 curr = 3;
326 for (i = 1; i <= 10; i++)
327 html_intoption(i, fmt("%d", i), curr);
328 for (i = 15; i <= 40; i += 5)
329 html_intoption(i, fmt("%d", i), curr);
330 html("</select>");
331 html("</td>");
332 html("</tr><tr>");
333 html("<td class='label'>space:</td>");
334 html("<td class='ctrl'>");
335 html("<select name='ignorews' onchange='this.form.submit();'>");
336 html_intoption(0, "include", ctx.qry.ignorews);
337 html_intoption(1, "ignore", ctx.qry.ignorews);
338 html("</select>");
339 html("</td>");
340 html("</tr><tr>");
341 html("<td class='label'>mode:</td>");
342 html("<td class='ctrl'>");
343 html("<select name='ss' onchange='this.form.submit();'>");
344 curr = ctx.qry.has_ssdiff ? ctx.qry.ssdiff : ctx.cfg.ssdiff;
345 html_intoption(0, "unified", curr);
346 html_intoption(1, "ssdiff", curr);
347 html("</select></td></tr>");
348 html("<tr><td/><td class='ctrl'>");
349 html("<noscript><input type='submit' value='reload'/></noscript>");
350 html("</td></tr></table>");
351 html("</form>");
352 html("</div>");
353}
354
355void cgit_print_diff(const char *new_rev, const char *old_rev,
356 const char *prefix, int show_ctrls)
336{ 357{
337 enum object_type type; 358 enum object_type type;
338 unsigned long size; 359 unsigned long size;
@@ -374,7 +395,9 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefi
374 395
375 use_ssdiff = ctx.qry.has_ssdiff ? ctx.qry.ssdiff : ctx.cfg.ssdiff; 396 use_ssdiff = ctx.qry.has_ssdiff ? ctx.qry.ssdiff : ctx.cfg.ssdiff;
376 397
377 print_ssdiff_link(); 398 if (show_ctrls)
399 cgit_print_diff_ctrls();
400
378 cgit_print_diffstat(old_rev_sha1, new_rev_sha1, prefix); 401 cgit_print_diffstat(old_rev_sha1, new_rev_sha1, prefix);
379 402
380 if (use_ssdiff) { 403 if (use_ssdiff) {
diff --git a/ui-diff.h b/ui-diff.h
index 12d0c62..0161ffb 100644
--- a/ui-diff.h
+++ b/ui-diff.h
@@ -1,11 +1,13 @@
1#ifndef UI_DIFF_H 1#ifndef UI_DIFF_H
2#define UI_DIFF_H 2#define UI_DIFF_H
3 3
4extern void cgit_print_diff_ctrls();
5
4extern void cgit_print_diffstat(const unsigned char *old_sha1, 6extern void cgit_print_diffstat(const unsigned char *old_sha1,
5 const unsigned char *new_sha1); 7 const unsigned char *new_sha1);
6 8
7extern void cgit_print_diff(const char *new_hex, const char *old_hex, 9extern void cgit_print_diff(const char *new_hex, const char *old_hex,
8 const char *prefix); 10 const char *prefix, int show_ctrls);
9 11
10extern struct diff_filespec *cgit_get_current_old_file(void); 12extern struct diff_filespec *cgit_get_current_old_file(void);
11extern struct diff_filespec *cgit_get_current_new_file(void); 13extern struct diff_filespec *cgit_get_current_new_file(void);
diff --git a/ui-log.c b/ui-log.c
index b67c2ef..6b12ca2 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -102,11 +102,10 @@ void print_commit(struct commit *commit, struct rev_info *revs)
102 struct strbuf graphbuf = STRBUF_INIT; 102 struct strbuf graphbuf = STRBUF_INIT;
103 struct strbuf msgbuf = STRBUF_INIT; 103 struct strbuf msgbuf = STRBUF_INIT;
104 104
105 if (ctx.repo->enable_log_filecount) { 105 if (ctx.repo->enable_log_filecount)
106 cols++;
107 if (ctx.repo->enable_log_linecount)
106 cols++; 108 cols++;
107 if (ctx.repo->enable_log_linecount)
108 cols++;
109 }
110 109
111 if (revs->graph) { 110 if (revs->graph) {
112 /* Advance graph until current commit */ 111 /* Advance graph until current commit */
@@ -173,18 +172,18 @@ void print_commit(struct commit *commit, struct rev_info *revs)
173 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 172 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
174 } 173 }
175 174
176 if (ctx.repo->enable_log_filecount) { 175 if (ctx.repo->enable_log_filecount || ctx.repo->enable_log_linecount) {
177 files = 0; 176 files = 0;
178 add_lines = 0; 177 add_lines = 0;
179 rem_lines = 0; 178 rem_lines = 0;
180 cgit_diff_commit(commit, inspect_files, ctx.qry.vpath); 179 cgit_diff_commit(commit, inspect_files, ctx.qry.vpath);
181 html("</td><td>");
182 htmlf("%d", files);
183 if (ctx.repo->enable_log_linecount) {
184 html("</td><td>");
185 htmlf("-%d/+%d", rem_lines, add_lines);
186 }
187 } 180 }
181
182 if (ctx.repo->enable_log_filecount)
183 htmlf("</td><td>%d", files);
184 if (ctx.repo->enable_log_linecount)
185 htmlf("</td><td>-%d/+%d", rem_lines, add_lines);
186
188 html("</td></tr>\n"); 187 html("</td></tr>\n");
189 188
190 if (revs->graph || ctx.qry.showmsg) { /* Print a second table row */ 189 if (revs->graph || ctx.qry.showmsg) { /* Print a second table row */
@@ -373,10 +372,10 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
373 if (ctx.repo->enable_log_filecount) { 372 if (ctx.repo->enable_log_filecount) {
374 html("<th class='left'>Files</th>"); 373 html("<th class='left'>Files</th>");
375 columns++; 374 columns++;
376 if (ctx.repo->enable_log_linecount) { 375 }
377 html("<th class='left'>Lines</th>"); 376 if (ctx.repo->enable_log_linecount) {
378 columns++; 377 html("<th class='left'>Lines</th>");
379 } 378 columns++;
380 } 379 }
381 html("</tr>\n"); 380 html("</tr>\n");
382 381
diff --git a/ui-repolist.c b/ui-repolist.c
index 4b21e2f..a09a689 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -46,11 +46,20 @@ static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
46 } 46 }
47 47
48 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch); 48 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
49 if (stat(path, &s) == 0) 49 if (stat(path, &s) == 0) {
50 *mtime = s.st_mtime; 50 *mtime = s.st_mtime;
51 else 51 r->mtime = *mtime;
52 *mtime = 0; 52 return 1;
53 }
54
55 path = fmt("%s/%s", repo->path, "packed-refs");
56 if (stat(path, &s) == 0) {
57 *mtime = s.st_mtime;
58 r->mtime = *mtime;
59 return 1;
60 }
53 61
62 *mtime = 0;
54 r->mtime = *mtime; 63 r->mtime = *mtime;
55 return (r->mtime != 0); 64 return (r->mtime != 0);
56} 65}
diff --git a/ui-stats.c b/ui-stats.c
index 2a0c174..59f4c1e 100644
--- a/ui-stats.c
+++ b/ui-stats.c
@@ -386,6 +386,33 @@ void cgit_show_stats(struct cgit_context *ctx)
386 top = ctx->qry.ofs; 386 top = ctx->qry.ofs;
387 if (!top) 387 if (!top)
388 top = 10; 388 top = 10;
389
390 html("<div class='cgit-panel'>");
391 html("<b>stat options</b>");
392 html("<form method='get' action=''>");
393 cgit_add_hidden_formfields(1, 0, "stats");
394 html("<table><tr><td colspan='2'/></tr>");
395 if (ctx->repo->max_stats > 1) {
396 html("<tr><td class='label'>Period:</td>");
397 html("<td class='ctrl'><select name='period' onchange='this.form.submit();'>");
398 for (i = 0; i < ctx->repo->max_stats; i++)
399 html_option(fmt("%c", periods[i].code),
400 periods[i].name, fmt("%c", period->code));
401 html("</select></td></tr>");
402 }
403 html("<tr><td class='label'>Authors:</td>");
404 html("<td class='ctrl'><select name='ofs' onchange='this.form.submit();'>");
405 html_intoption(10, "10", top);
406 html_intoption(25, "25", top);
407 html_intoption(50, "50", top);
408 html_intoption(100, "100", top);
409 html_intoption(-1, "all", top);
410 html("</select></td></tr>");
411 html("<tr><td/><td class='ctrl'>");
412 html("<noscript><input type='submit' value='Reload'/></noscript>");
413 html("</td></tr></table>");
414 html("</form>");
415 html("</div>");
389 htmlf("<h2>Commits per author per %s", period->name); 416 htmlf("<h2>Commits per author per %s", period->name);
390 if (ctx->qry.path) { 417 if (ctx->qry.path) {
391 html(" (path '"); 418 html(" (path '");
@@ -393,30 +420,6 @@ void cgit_show_stats(struct cgit_context *ctx)
393 html("')"); 420 html("')");
394 } 421 }
395 html("</h2>"); 422 html("</h2>");
396
397 html("<form method='get' action='' style='float: right; text-align: right;'>");
398 cgit_add_hidden_formfields(1, 0, "stats");
399 if (ctx->repo->max_stats > 1) {
400 html("Period: ");
401 html("<select name='period' onchange='this.form.submit();'>");
402 for (i = 0; i < ctx->repo->max_stats; i++)
403 htmlf("<option value='%c'%s>%s</option>",
404 periods[i].code,
405 period == &periods[i] ? " selected" : "",
406 periods[i].name);
407 html("</select><br/><br/>");
408 }
409 html("Authors: ");
410 html("");
411 html("<select name='ofs' onchange='this.form.submit();'>");
412 htmlf("<option value='10'%s>10</option>", top == 10 ? " selected" : "");
413 htmlf("<option value='25'%s>25</option>", top == 25 ? " selected" : "");
414 htmlf("<option value='50'%s>50</option>", top == 50 ? " selected" : "");
415 htmlf("<option value='100'%s>100</option>", top == 100 ? " selected" : "");
416 htmlf("<option value='-1'%s>All</option>", top == -1 ? " selected" : "");
417 html("</select>");
418 html("<noscript>&nbsp;&nbsp;<input type='submit' value='Reload'/></noscript>");
419 html("</form>");
420 print_authors(&authors, top, period); 423 print_authors(&authors, top, period);
421} 424}
422 425
diff --git a/ui-summary.c b/ui-summary.c
index 5be2545..227ed27 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -62,7 +62,7 @@ void cgit_print_summary()
62 NULL, NULL, 0, 0); 62 NULL, NULL, 0, 0);
63 } 63 }
64 if (ctx.repo->clone_url) 64 if (ctx.repo->clone_url)
65 print_urls(ctx.repo->clone_url, NULL); 65 print_urls(expand_macros(ctx.repo->clone_url), NULL);
66 else if (ctx.cfg.clone_prefix) 66 else if (ctx.cfg.clone_prefix)
67 print_urls(ctx.cfg.clone_prefix, ctx.repo->url); 67 print_urls(ctx.cfg.clone_prefix, ctx.repo->url);
68 html("</table>"); 68 html("</table>");