aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--README9
-rw-r--r--cgit.c28
-rw-r--r--cgit.css42
-rw-r--r--cgit.h6
-rw-r--r--cgit.pngbin1840 -> 1488 bytes
-rw-r--r--cgitrc.5.txt40
-rw-r--r--cmd.c3
m---------git0
-rw-r--r--html.c6
-rw-r--r--parsing.c24
-rw-r--r--scan-tree.c21
-rw-r--r--shared.c1
-rw-r--r--ui-diff.c19
-rw-r--r--ui-diff.h6
-rw-r--r--ui-log.c287
-rw-r--r--ui-log.h3
-rw-r--r--ui-shared.c20
-rw-r--r--ui-ssdiff.c34
-rw-r--r--ui-summary.c2
-rw-r--r--ui-tree.c2
-rw-r--r--vector.c38
-rw-r--r--vector.h17
23 files changed, 485 insertions, 130 deletions
diff --git a/Makefile b/Makefile
index fe4b10e..af2879e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
1CGIT_VERSION = v0.8.3.4 1CGIT_VERSION = v0.9
2CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit 3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH) 4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
@@ -12,7 +12,7 @@ htmldir = $(docdir)
12pdfdir = $(docdir) 12pdfdir = $(docdir)
13mandir = $(prefix)/share/man 13mandir = $(prefix)/share/man
14SHA1_HEADER = <openssl/sha.h> 14SHA1_HEADER = <openssl/sha.h>
15GIT_VER = 1.7.3 15GIT_VER = 1.7.4
16GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 16GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
17INSTALL = install 17INSTALL = install
18MAN5_TXT = $(wildcard *.5.txt) 18MAN5_TXT = $(wildcard *.5.txt)
@@ -115,6 +115,7 @@ OBJECTS += ui-stats.o
115OBJECTS += ui-summary.o 115OBJECTS += ui-summary.o
116OBJECTS += ui-tag.o 116OBJECTS += ui-tag.o
117OBJECTS += ui-tree.o 117OBJECTS += ui-tree.o
118OBJECTS += vector.o
118 119
119ifdef NEEDS_LIBICONV 120ifdef NEEDS_LIBICONV
120 EXTLIBS += -liconv 121 EXTLIBS += -liconv
@@ -240,4 +241,4 @@ clean-doc:
240 rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo 241 rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
241 242
242get-git: 243get-git:
243 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git 244 curl $(GIT_URL) | tar -xjf - && rm -rf git && mv git-$(GIT_VER) git
diff --git a/README b/README
index 050e21e..1692ad6 100644
--- a/README
+++ b/README
@@ -36,7 +36,7 @@ file (see the Makefile for details).
36 36
37 37
38Dependencies: 38Dependencies:
39 -git 1.5.3 39 -git 1.7.4
40 -zip lib 40 -zip lib
41 -crypto lib 41 -crypto lib
42 -openssl lib 42 -openssl lib
@@ -86,13 +86,6 @@ The missing features
86 repository. This should probably be extended to a generic map between 86 repository. This should probably be extended to a generic map between
87 submodule path and url. 87 submodule path and url.
88 88
89* Branch- and tag-lists in the summary page can get very long, they should
90 probably only show something like the ten "latest modified" branches and
91 a similar number of "most recent" tags.
92
93* There should be a new page for browsing refs/heads and refs/tags, with links
94 from the summary page whenever the branch/tag lists overflow.
95
96* The log-page should have more/better search options (author, committer, 89* The log-page should have more/better search options (author, committer,
97 pickaxe, paths) and possibly support arbitrary revision specifiers. 90 pickaxe, paths) and possibly support arbitrary revision specifiers.
98 91
diff --git a/cgit.c b/cgit.c
index e0c2d9f..349d6e0 100644
--- a/cgit.c
+++ b/cgit.c
@@ -29,15 +29,17 @@ void add_mimetype(const char *name, const char *value)
29struct cgit_filter *new_filter(const char *cmd, int extra_args) 29struct cgit_filter *new_filter(const char *cmd, int extra_args)
30{ 30{
31 struct cgit_filter *f; 31 struct cgit_filter *f;
32 int args_size = 0;
32 33
33 if (!cmd || !cmd[0]) 34 if (!cmd || !cmd[0])
34 return NULL; 35 return NULL;
35 36
36 f = xmalloc(sizeof(struct cgit_filter)); 37 f = xmalloc(sizeof(struct cgit_filter));
37 f->cmd = xstrdup(cmd); 38 f->cmd = xstrdup(cmd);
38 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 39 args_size = (2 + extra_args) * sizeof(char *);
40 f->argv = xmalloc(args_size);
41 memset(f->argv, 0, args_size);
39 f->argv[0] = f->cmd; 42 f->argv[0] = f->cmd;
40 f->argv[1] = NULL;
41 return f; 43 return f;
42} 44}
43 45
@@ -57,6 +59,8 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
57 repo->defbranch = xstrdup(value); 59 repo->defbranch = xstrdup(value);
58 else if (!strcmp(name, "snapshots")) 60 else if (!strcmp(name, "snapshots"))
59 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 61 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
62 else if (!strcmp(name, "enable-commit-graph"))
63 repo->enable_commit_graph = ctx.cfg.enable_commit_graph * atoi(value);
60 else if (!strcmp(name, "enable-log-filecount")) 64 else if (!strcmp(name, "enable-log-filecount"))
61 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 65 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
62 else if (!strcmp(name, "enable-log-linecount")) 66 else if (!strcmp(name, "enable-log-linecount"))
@@ -71,9 +75,13 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
71 repo->module_link= xstrdup(value); 75 repo->module_link= xstrdup(value);
72 else if (!strcmp(name, "section")) 76 else if (!strcmp(name, "section"))
73 repo->section = xstrdup(value); 77 repo->section = xstrdup(value);
74 else if (!strcmp(name, "readme") && value != NULL) { 78 else if (!strcmp(name, "readme") && value != NULL)
75 repo->readme = xstrdup(value); 79 repo->readme = xstrdup(value);
76 } else if (ctx.cfg.enable_filter_overrides) { 80 else if (!strcmp(name, "logo") && value != NULL)
81 repo->logo = xstrdup(value);
82 else if (!strcmp(name, "logo-link") && value != NULL)
83 repo->logo_link = xstrdup(value);
84 else if (ctx.cfg.enable_filter_overrides) {
77 if (!strcmp(name, "about-filter")) 85 if (!strcmp(name, "about-filter"))
78 repo->about_filter = new_filter(value, 0); 86 repo->about_filter = new_filter(value, 0);
79 else if (!strcmp(name, "commit-filter")) 87 else if (!strcmp(name, "commit-filter"))
@@ -143,6 +151,8 @@ void config_cb(const char *name, const char *value)
143 ctx.cfg.enable_http_clone = atoi(value); 151 ctx.cfg.enable_http_clone = atoi(value);
144 else if (!strcmp(name, "enable-index-links")) 152 else if (!strcmp(name, "enable-index-links"))
145 ctx.cfg.enable_index_links = atoi(value); 153 ctx.cfg.enable_index_links = atoi(value);
154 else if (!strcmp(name, "enable-commit-graph"))
155 ctx.cfg.enable_commit_graph = atoi(value);
146 else if (!strcmp(name, "enable-log-filecount")) 156 else if (!strcmp(name, "enable-log-filecount"))
147 ctx.cfg.enable_log_filecount = atoi(value); 157 ctx.cfg.enable_log_filecount = atoi(value);
148 else if (!strcmp(name, "enable-log-linecount")) 158 else if (!strcmp(name, "enable-log-linecount"))
@@ -197,6 +207,8 @@ void config_cb(const char *name, const char *value)
197 ctx.cfg.project_list, repo_config); 207 ctx.cfg.project_list, repo_config);
198 else 208 else
199 scan_tree(expand_macros(value), repo_config); 209 scan_tree(expand_macros(value), repo_config);
210 else if (!strcmp(name, "scan-hidden-path"))
211 ctx.cfg.scan_hidden_path = atoi(value);
200 else if (!strcmp(name, "section-from-path")) 212 else if (!strcmp(name, "section-from-path"))
201 ctx.cfg.section_from_path = atoi(value); 213 ctx.cfg.section_from_path = atoi(value);
202 else if (!strcmp(name, "source-filter")) 214 else if (!strcmp(name, "source-filter"))
@@ -318,6 +330,7 @@ static void prepare_context(struct cgit_context *ctx)
318 ctx->cfg.robots = "index, nofollow"; 330 ctx->cfg.robots = "index, nofollow";
319 ctx->cfg.root_title = "Git repository browser"; 331 ctx->cfg.root_title = "Git repository browser";
320 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 332 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
333 ctx->cfg.scan_hidden_path = 0;
321 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 334 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
322 ctx->cfg.section = ""; 335 ctx->cfg.section = "";
323 ctx->cfg.summary_branches = 10; 336 ctx->cfg.summary_branches = 10;
@@ -550,6 +563,8 @@ void print_repo(FILE *f, struct cgit_repo *repo)
550 fprintf(f, "repo.section=%s\n", repo->section); 563 fprintf(f, "repo.section=%s\n", repo->section);
551 if (repo->clone_url) 564 if (repo->clone_url)
552 fprintf(f, "repo.clone-url=%s\n", repo->clone_url); 565 fprintf(f, "repo.clone-url=%s\n", repo->clone_url);
566 fprintf(f, "repo.enable-commit-graph=%d\n",
567 repo->enable_commit_graph);
553 fprintf(f, "repo.enable-log-filecount=%d\n", 568 fprintf(f, "repo.enable-log-filecount=%d\n",
554 repo->enable_log_filecount); 569 repo->enable_log_filecount);
555 fprintf(f, "repo.enable-log-linecount=%d\n", 570 fprintf(f, "repo.enable-log-linecount=%d\n",
@@ -749,10 +764,11 @@ int main(int argc, const char **argv)
749 http_parse_querystring(ctx.qry.raw, querystring_cb); 764 http_parse_querystring(ctx.qry.raw, querystring_cb);
750 765
751 /* If virtual-root isn't specified in cgitrc, lets pretend 766 /* If virtual-root isn't specified in cgitrc, lets pretend
752 * that virtual-root equals SCRIPT_NAME. 767 * that virtual-root equals SCRIPT_NAME, minus any possibly
768 * trailing slashes.
753 */ 769 */
754 if (!ctx.cfg.virtual_root) 770 if (!ctx.cfg.virtual_root)
755 ctx.cfg.virtual_root = ctx.cfg.script_name; 771 ctx.cfg.virtual_root = trim_end(ctx.cfg.script_name, '/');
756 772
757 /* If no url parameter is specified on the querystring, lets 773 /* If no url parameter is specified on the querystring, lets
758 * use PATH_INFO as url. This allows cgit to work with virtual 774 * use PATH_INFO as url. This allows cgit to work with virtual
diff --git a/cgit.css b/cgit.css
index a2a685b..1d90057 100644
--- a/cgit.css
+++ b/cgit.css
@@ -153,26 +153,44 @@ table.list td {
153 padding: 0.1em 0.5em 0.1em 0.5em; 153 padding: 0.1em 0.5em 0.1em 0.5em;
154} 154}
155 155
156table.list td.logsubject { 156table.list td.commitgraph {
157 font-family: monospace; 157 font-family: monospace;
158 font-weight: bold; 158 white-space: pre;
159} 159}
160 160
161table.list td.logmsg { 161table.list td.commitgraph .column1 {
162 font-family: monospace; 162 color: #a00;
163 white-space: pre; 163}
164 padding: 1em 0.5em 2em 0.5em; 164
165table.list td.commitgraph .column2 {
166 color: #0a0;
167}
168
169table.list td.commitgraph .column3 {
170 color: #aa0;
165} 171}
166 172
167table.list td.lognotes-label { 173table.list td.commitgraph .column4 {
168 text-align:right; 174 color: #00a;
169 vertical-align:top;
170} 175}
171 176
172table.list td.lognotes { 177table.list td.commitgraph .column5 {
178 color: #a0a;
179}
180
181table.list td.commitgraph .column6 {
182 color: #0aa;
183}
184
185table.list td.logsubject {
186 font-family: monospace;
187 font-weight: bold;
188}
189
190table.list td.logmsg {
173 font-family: monospace; 191 font-family: monospace;
174 white-space: pre; 192 white-space: pre;
175 padding: 0em 0.5em 2em 0.5em; 193 padding: 0 0.5em;
176} 194}
177 195
178table.list td a { 196table.list td a {
@@ -275,7 +293,7 @@ table.blob pre {
275 padding: 0; margin: 0; 293 padding: 0; margin: 0;
276} 294}
277 295
278table.blob a.no { 296table.blob a.no, table.ssdiff a.no {
279 color: gray; 297 color: gray;
280 text-align: right; 298 text-align: right;
281 text-decoration: none; 299 text-decoration: none;
diff --git a/cgit.h b/cgit.h
index c119712..ecae453 100644
--- a/cgit.h
+++ b/cgit.h
@@ -20,6 +20,7 @@
20#include <xdiff/xdiff.h> 20#include <xdiff/xdiff.h>
21#include <utf8.h> 21#include <utf8.h>
22#include <notes.h> 22#include <notes.h>
23#include <graph.h>
23 24
24 25
25/* 26/*
@@ -70,7 +71,10 @@ struct cgit_repo {
70 char *readme; 71 char *readme;
71 char *section; 72 char *section;
72 char *clone_url; 73 char *clone_url;
74 char *logo;
75 char *logo_link;
73 int snapshots; 76 int snapshots;
77 int enable_commit_graph;
74 int enable_log_filecount; 78 int enable_log_filecount;
75 int enable_log_linecount; 79 int enable_log_linecount;
76 int enable_remote_branches; 80 int enable_remote_branches;
@@ -189,6 +193,7 @@ struct cgit_config {
189 int enable_gitweb_owner; 193 int enable_gitweb_owner;
190 int enable_http_clone; 194 int enable_http_clone;
191 int enable_index_links; 195 int enable_index_links;
196 int enable_commit_graph;
192 int enable_log_filecount; 197 int enable_log_filecount;
193 int enable_log_linecount; 198 int enable_log_linecount;
194 int enable_remote_branches; 199 int enable_remote_branches;
@@ -208,6 +213,7 @@ struct cgit_config {
208 int noheader; 213 int noheader;
209 int renamelimit; 214 int renamelimit;
210 int remove_suffix; 215 int remove_suffix;
216 int scan_hidden_path;
211 int section_from_path; 217 int section_from_path;
212 int snapshots; 218 int snapshots;
213 int summary_branches; 219 int summary_branches;
diff --git a/cgit.png b/cgit.png
index d7f70bc..0bdf5a7 100644
--- a/cgit.png
+++ b/cgit.png
Binary files differ
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index b8c69b8..875d51f 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -90,7 +90,12 @@ embedded::
90 Flag which, when set to "1", will make cgit generate a html fragment 90 Flag which, when set to "1", will make cgit generate a html fragment
91 suitable for embedding in other html pages. Default value: none. See 91 suitable for embedding in other html pages. Default value: none. See
92 also: "noheader". 92 also: "noheader".
93 93
94enable-commit-graph::
95 Flag which, when set to "1", will make cgit print an ASCII-art commit
96 history graph to the left of the commit messages in the repository
97 log page. Default value: "0".
98
94enable-filter-overrides:: 99enable-filter-overrides::
95 Flag which, when set to "1", allows all filter settings to be 100 Flag which, when set to "1", allows all filter settings to be
96 overridden in repository-specific cgitrc files. Default value: none. 101 overridden in repository-specific cgitrc files. Default value: none.
@@ -274,13 +279,22 @@ root-title::
274 Text printed as heading on the repository index page. Default value: 279 Text printed as heading on the repository index page. Default value:
275 "Git Repository Browser". 280 "Git Repository Browser".
276 281
282scan-hidden-path::
283 If set to "1" and scan-path is enabled, scan-path will recurse into
284 directories whose name starts with a period ('.'). Otherwise,
285 scan-path will stay away from such directories (considered as
286 "hidden"). Note that this does not apply to the ".git" directory in
287 non-bare repos. This must be defined prior to scan-path.
288 Default value: 0. See also: scan-path.
289
277scan-path:: 290scan-path::
278 A path which will be scanned for repositories. If caching is enabled, 291 A path which will be scanned for repositories. If caching is enabled,
279 the result will be cached as a cgitrc include-file in the cache 292 the result will be cached as a cgitrc include-file in the cache
280 directory. If project-list has been defined prior to scan-path, 293 directory. If project-list has been defined prior to scan-path,
281 scan-path loads only the directories listed in the file pointed to by 294 scan-path loads only the directories listed in the file pointed to by
282 project-list. Default value: none. See also: cache-scanrc-ttl, 295 project-list. Be advised that only the global settings taken
283 project-list. 296 before the scan-path directive will be applied to each repository.
297 Default value: none. See also: cache-scanrc-ttl, project-list.
284 298
285section:: 299section::
286 The name of the current repository section - all repositories defined 300 The name of the current repository section - all repositories defined
@@ -300,7 +314,8 @@ side-by-side-diffs::
300snapshots:: 314snapshots::
301 Text which specifies the default set of snapshot formats generated by 315 Text which specifies the default set of snapshot formats generated by
302 cgit. The value is a space-separated list of zero or more of the 316 cgit. The value is a space-separated list of zero or more of the
303 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 317 values "tar", "tar.gz", "tar.bz2", "tar.xz" and "zip". Default value:
318 none.
304 319
305source-filter:: 320source-filter::
306 Specifies a command which will be invoked to format plaintext blobs 321 Specifies a command which will be invoked to format plaintext blobs
@@ -359,6 +374,10 @@ repo.defbranch::
359repo.desc:: 374repo.desc::
360 The value to show as repository description. Default value: none. 375 The value to show as repository description. Default value: none.
361 376
377repo.enable-commit-graph::
378 A flag which can be used to disable the global setting
379 `enable-commit-graph'. Default value: none.
380
362repo.enable-log-filecount:: 381repo.enable-log-filecount::
363 A flag which can be used to disable the global setting 382 A flag which can be used to disable the global setting
364 `enable-log-filecount'. Default value: none. 383 `enable-log-filecount'. Default value: none.
@@ -375,6 +394,15 @@ repo.enable-subject-links::
375 A flag which can be used to override the global setting 394 A flag which can be used to override the global setting
376 `enable-subject-links'. Default value: none. 395 `enable-subject-links'. Default value: none.
377 396
397repo.logo::
398 Url which specifies the source of an image which will be used as a logo
399 on this repo's pages. Default value: global logo.
400
401repo.logo-link::
402 Url loaded when clicking on the cgit logo image. If unspecified the
403 calculated url of the repository index page will be used. Default
404 value: global logo-link.
405
378repo.max-stats:: 406repo.max-stats::
379 Override the default maximum statistics period. Valid values are equal 407 Override the default maximum statistics period. Valid values are equal
380 to the values specified for the global "max-stats" setting. Default 408 to the values specified for the global "max-stats" setting. Default
@@ -446,6 +474,10 @@ css=/css/cgit.css
446enable-index-links=1 474enable-index-links=1
447 475
448 476
477# Enable ASCII art commit history graph on the log pages
478enable-commit-graph=1
479
480
449# Show number of affected files per commit on the log pages 481# Show number of affected files per commit on the log pages
450enable-log-filecount=1 482enable-log-filecount=1
451 483
diff --git a/cmd.c b/cmd.c
index 0224ee9..d114eb3 100644
--- a/cmd.c
+++ b/cmd.c
@@ -67,7 +67,8 @@ static void info_fn(struct cgit_context *ctx)
67static void log_fn(struct cgit_context *ctx) 67static void log_fn(struct cgit_context *ctx)
68{ 68{
69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, 69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); 70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1,
71 ctx->repo->enable_commit_graph);
71} 72}
72 73
73static void ls_cache_fn(struct cgit_context *ctx) 74static void ls_cache_fn(struct cgit_context *ctx)
diff --git a/git b/git
Subproject 87b50542a08ac6caa083ddc376e674424e37940 Subproject 7ed863a85a6ce2c4ac4476848310b8f917ab41f
diff --git a/html.c b/html.c
index 1305910..a60bc13 100644
--- a/html.c
+++ b/html.c
@@ -18,7 +18,7 @@ static const char* url_escape_table[256] = {
18 "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09", 18 "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09",
19 "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13", 19 "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13",
20 "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d", 20 "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d",
21 "%1e", "%1f", "%20", 0, "%22", "%23", 0, "%25", "%26", "%27", 0, 0, 0, 21 "%1e", "%1f", "+", 0, "%22", "%23", 0, "%25", "%26", "%27", 0, 0, 0,
22 "%2b", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%3c", "%3d", 22 "%2b", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%3c", "%3d",
23 "%3e", "%3f", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23 "%3e", "%3f", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24 0, 0, 0, 0, 0, 0, 0, 0, 0, "%5c", 0, "%5e", 0, "%60", 0, 0, 0, 0, 0, 24 0, 0, 0, 0, 0, 0, 0, 0, 0, "%5c", 0, "%5e", 0, "%60", 0, 0, 0, 0, 0,
@@ -181,7 +181,7 @@ void html_url_arg(const char *txt)
181 const char *e = url_escape_table[c]; 181 const char *e = url_escape_table[c];
182 if (e) { 182 if (e) {
183 html_raw(txt, t - txt); 183 html_raw(txt, t - txt);
184 html_raw(e, 3); 184 html_raw(e, strlen(e));
185 txt = t+1; 185 txt = t+1;
186 } 186 }
187 t++; 187 t++;
@@ -277,7 +277,7 @@ char *convert_query_hexchar(char *txt)
277 d1 = hextoint(*(txt+1)); 277 d1 = hextoint(*(txt+1));
278 d2 = hextoint(*(txt+2)); 278 d2 = hextoint(*(txt+2));
279 if (d1<0 || d2<0) { 279 if (d1<0 || d2<0) {
280 memmove(txt, txt+3, n-3); 280 memmove(txt, txt+3, n-2);
281 return txt-1; 281 return txt-1;
282 } else { 282 } else {
283 *txt = d1 * 16 + d2; 283 *txt = d1 * 16 + d2;
diff --git a/parsing.c b/parsing.c
index f37c49d..c9e4350 100644
--- a/parsing.c
+++ b/parsing.c
@@ -106,7 +106,11 @@ const char *reencode(char **txt, const char *src_enc, const char *dst_enc)
106 if (!txt || !*txt || !src_enc || !dst_enc) 106 if (!txt || !*txt || !src_enc || !dst_enc)
107 return *txt; 107 return *txt;
108 108
109 tmp = reencode_string(*txt, src_enc, dst_enc); 109 /* no encoding needed if src_enc equals dst_enc */
110 if(!strcasecmp(src_enc, dst_enc))
111 return *txt;
112
113 tmp = reencode_string(*txt, dst_enc, src_enc);
110 if (tmp) { 114 if (tmp) {
111 free(*txt); 115 free(*txt);
112 *txt = tmp; 116 *txt = tmp;
@@ -160,6 +164,10 @@ struct commitinfo *cgit_parse_commit(struct commit *commit)
160 } 164 }
161 } 165 }
162 166
167 /* if no special encoding is found, assume UTF-8 */
168 if(!ret->msg_encoding)
169 ret->msg_encoding = xstrdup("UTF-8");
170
163 // skip unknown header fields 171 // skip unknown header fields
164 while (p && *p && (*p != '\n')) { 172 while (p && *p && (*p != '\n')) {
165 p = strchr(p, '\n'); 173 p = strchr(p, '\n');
@@ -189,14 +197,12 @@ struct commitinfo *cgit_parse_commit(struct commit *commit)
189 } else 197 } else
190 ret->subject = xstrdup(p); 198 ret->subject = xstrdup(p);
191 199
192 if (ret->msg_encoding) { 200 reencode(&ret->author, ret->msg_encoding, PAGE_ENCODING);
193 reencode(&ret->author, PAGE_ENCODING, ret->msg_encoding); 201 reencode(&ret->author_email, ret->msg_encoding, PAGE_ENCODING);
194 reencode(&ret->author_email, PAGE_ENCODING, ret->msg_encoding); 202 reencode(&ret->committer, ret->msg_encoding, PAGE_ENCODING);
195 reencode(&ret->committer, PAGE_ENCODING, ret->msg_encoding); 203 reencode(&ret->committer_email, ret->msg_encoding, PAGE_ENCODING);
196 reencode(&ret->committer_email, PAGE_ENCODING, ret->msg_encoding); 204 reencode(&ret->subject, ret->msg_encoding, PAGE_ENCODING);
197 reencode(&ret->subject, PAGE_ENCODING, ret->msg_encoding); 205 reencode(&ret->msg, ret->msg_encoding, PAGE_ENCODING);
198 reencode(&ret->msg, PAGE_ENCODING, ret->msg_encoding);
199 }
200 206
201 return ret; 207 return ret;
202} 208}
diff --git a/scan-tree.c b/scan-tree.c
index a0e09ce..e5a4baf 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -159,24 +159,23 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
159 159
160static void scan_path(const char *base, const char *path, repo_config_fn fn) 160static void scan_path(const char *base, const char *path, repo_config_fn fn)
161{ 161{
162 DIR *dir; 162 DIR *dir = opendir(path);
163 struct dirent *ent; 163 struct dirent *ent;
164 char *buf; 164 char *buf;
165 struct stat st; 165 struct stat st;
166 166
167 if (!dir) {
168 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
169 path, strerror(errno), errno);
170 return;
171 }
167 if (is_git_dir(path)) { 172 if (is_git_dir(path)) {
168 add_repo(base, path, fn); 173 add_repo(base, path, fn);
169 return; 174 goto end;
170 } 175 }
171 if (is_git_dir(fmt("%s/.git", path))) { 176 if (is_git_dir(fmt("%s/.git", path))) {
172 add_repo(base, fmt("%s/.git", path), fn); 177 add_repo(base, fmt("%s/.git", path), fn);
173 return; 178 goto end;
174 }
175 dir = opendir(path);
176 if (!dir) {
177 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
178 path, strerror(errno), errno);
179 return;
180 } 179 }
181 while((ent = readdir(dir)) != NULL) { 180 while((ent = readdir(dir)) != NULL) {
182 if (ent->d_name[0] == '.') { 181 if (ent->d_name[0] == '.') {
@@ -184,6 +183,8 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn)
184 continue; 183 continue;
185 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 184 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
186 continue; 185 continue;
186 if (!ctx.cfg.scan_hidden_path)
187 continue;
187 } 188 }
188 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 189 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
189 if (!buf) { 190 if (!buf) {
@@ -202,6 +203,7 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn)
202 scan_path(base, buf, fn); 203 scan_path(base, buf, fn);
203 free(buf); 204 free(buf);
204 } 205 }
206end:
205 closedir(dir); 207 closedir(dir);
206} 208}
207 209
@@ -217,6 +219,7 @@ void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn
217 if (!projects) { 219 if (!projects) {
218 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n", 220 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
219 projectsfile, strerror(errno), errno); 221 projectsfile, strerror(errno), errno);
222 return;
220 } 223 }
221 while (fgets(line, sizeof(line), projects) != NULL) { 224 while (fgets(line, sizeof(line), projects) != NULL) {
222 for (z = &lastc(line); 225 for (z = &lastc(line);
diff --git a/shared.c b/shared.c
index 765cd27..7ec2e19 100644
--- a/shared.c
+++ b/shared.c
@@ -56,6 +56,7 @@ struct cgit_repo *cgit_add_repo(const char *url)
56 ret->section = ctx.cfg.section; 56 ret->section = ctx.cfg.section;
57 ret->defbranch = "master"; 57 ret->defbranch = "master";
58 ret->snapshots = ctx.cfg.snapshots; 58 ret->snapshots = ctx.cfg.snapshots;
59 ret->enable_commit_graph = ctx.cfg.enable_commit_graph;
59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 60 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 61 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->enable_remote_branches = ctx.cfg.enable_remote_branches; 62 ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
diff --git a/ui-diff.c b/ui-diff.c
index 7ff7e46..a7bc667 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -34,6 +34,17 @@ static struct fileinfo {
34} *items; 34} *items;
35 35
36static int use_ssdiff = 0; 36static int use_ssdiff = 0;
37static struct diff_filepair *current_filepair;
38
39struct diff_filespec *cgit_get_current_old_file(void)
40{
41 return current_filepair->one;
42}
43
44struct diff_filespec *cgit_get_current_new_file(void)
45{
46 return current_filepair->two;
47}
37 48
38static void print_fileinfo(struct fileinfo *info) 49static void print_fileinfo(struct fileinfo *info)
39{ 50{
@@ -161,8 +172,11 @@ void cgit_print_diffstat(const unsigned char *old_sha1,
161 html("<div class='diffstat-header'>"); 172 html("<div class='diffstat-header'>");
162 cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, 173 cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
163 ctx.qry.sha2, NULL, 0); 174 ctx.qry.sha2, NULL, 0);
164 if (prefix) 175 if (prefix) {
165 htmlf(" (limited to '%s')", prefix); 176 html(" (limited to '");
177 html_txt(prefix);
178 html("')");
179 }
166 html(" ("); 180 html(" (");
167 ctx.qry.context = (save_context > 0 ? save_context : 3) << 1; 181 ctx.qry.context = (save_context > 0 ? save_context : 3) << 1;
168 cgit_self_link("more", NULL, NULL, &ctx); 182 cgit_self_link("more", NULL, NULL, &ctx);
@@ -284,6 +298,7 @@ static void filepair_cb(struct diff_filepair *pair)
284 int binary = 0; 298 int binary = 0;
285 linediff_fn print_line_fn = print_line; 299 linediff_fn print_line_fn = print_line;
286 300
301 current_filepair = pair;
287 if (use_ssdiff) { 302 if (use_ssdiff) {
288 cgit_ssdiff_header_begin(); 303 cgit_ssdiff_header_begin();
289 print_line_fn = cgit_ssdiff_line_cb; 304 print_line_fn = cgit_ssdiff_line_cb;
diff --git a/ui-diff.h b/ui-diff.h
index 70b2926..12d0c62 100644
--- a/ui-diff.h
+++ b/ui-diff.h
@@ -7,4 +7,10 @@ extern void cgit_print_diffstat(const unsigned char *old_sha1,
7extern void cgit_print_diff(const char *new_hex, const char *old_hex, 7extern void cgit_print_diff(const char *new_hex, const char *old_hex,
8 const char *prefix); 8 const char *prefix);
9 9
10extern struct diff_filespec *cgit_get_current_old_file(void);
11extern struct diff_filespec *cgit_get_current_new_file(void);
12
13extern unsigned char old_rev_sha1[20];
14extern unsigned char new_rev_sha1[20];
15
10#endif /* UI_DIFF_H */ 16#endif /* UI_DIFF_H */
diff --git a/ui-log.c b/ui-log.c
index b9771fa..2e6e9d6 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -9,9 +9,25 @@
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12#include "vector.h"
12 13
13int files, add_lines, rem_lines; 14int files, add_lines, rem_lines;
14 15
16/*
17 * The list of available column colors in the commit graph.
18 */
19static const char *column_colors_html[] = {
20 "<span class='column1'>",
21 "<span class='column2'>",
22 "<span class='column3'>",
23 "<span class='column4'>",
24 "<span class='column5'>",
25 "<span class='column6'>",
26 "</span>",
27};
28
29#define COLUMN_COLORS_HTML_MAX (ARRAY_SIZE(column_colors_html) - 1)
30
15void count_lines(char *line, int size) 31void count_lines(char *line, int size)
16{ 32{
17 if (size <= 0) 33 if (size <= 0)
@@ -76,63 +92,160 @@ void show_commit_decorations(struct commit *commit)
76 } 92 }
77} 93}
78 94
79void print_commit(struct commit *commit) 95void print_commit(struct commit *commit, struct rev_info *revs)
80{ 96{
81 struct commitinfo *info; 97 struct commitinfo *info;
82 char *tmp; 98 char *tmp;
83 int cols = 2; 99 int cols = revs->graph ? 3 : 2;
100 struct strbuf graphbuf = STRBUF_INIT;
101 struct strbuf msgbuf = STRBUF_INIT;
102
103 if (ctx.repo->enable_log_filecount)
104 cols++;
105 if (ctx.repo->enable_log_linecount)
106 cols++;
107
108 if (revs->graph) {
109 /* Advance graph until current commit */
110 while (!graph_next_line(revs->graph, &graphbuf)) {
111 /* Print graph segment in otherwise empty table row */
112 html("<tr class='nohover'><td class='commitgraph'>");
113 html(graphbuf.buf);
114 htmlf("</td><td colspan='%d' /></tr>\n", cols);
115 strbuf_setlen(&graphbuf, 0);
116 }
117 /* Current commit's graph segment is now ready in graphbuf */
118 }
84 119
85 info = cgit_parse_commit(commit); 120 info = cgit_parse_commit(commit);
86 htmlf("<tr%s><td>", 121 htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : "");
87 ctx.qry.showmsg ? " class='logheader'" : ""); 122
88 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); 123 if (revs->graph) {
89 tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp); 124 /* Print graph segment for current commit */
90 html_link_open(tmp, NULL, NULL); 125 html("<td class='commitgraph'>");
91 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 126 html(graphbuf.buf);
92 html_link_close(); 127 html("</td>");
93 htmlf("</td><td%s>", 128 strbuf_setlen(&graphbuf, 0);
94 ctx.qry.showmsg ? " class='logsubject'" : ""); 129 }
130 else {
131 html("<td>");
132 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
133 tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp);
134 html_link_open(tmp, NULL, NULL);
135 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
136 html_link_close();
137 html("</td>");
138 }
139
140 htmlf("<td%s>", ctx.qry.showmsg ? " class='logsubject'" : "");
141 if (ctx.qry.showmsg) {
142 /* line-wrap long commit subjects instead of truncating them */
143 size_t subject_len = strlen(info->subject);
144
145 if (subject_len > ctx.cfg.max_msg_len &&
146 ctx.cfg.max_msg_len >= 15) {
147 /* symbol for signaling line-wrap (in PAGE_ENCODING) */
148 const ch