summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile4
-rw-r--r--cgit.c41
-rw-r--r--cgit.css24
-rwxr-xr-xtests/t0104-tree.sh2
-rw-r--r--ui-log.c35
-rw-r--r--ui-patch.c4
-rw-r--r--ui-refs.c2
-rw-r--r--ui-tree.c6
8 files changed, 90 insertions, 28 deletions
diff --git a/Makefile b/Makefile
index 3c7ec07..036fcd7 100644
--- a/Makefile
+++ b/Makefile
@@ -107,46 +107,46 @@ VERSION: force-version
107 107
108 108
109CFLAGS += -g -Wall -Igit 109CFLAGS += -g -Wall -Igit
110CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 110CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
111CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 111CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
112CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 112CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
113CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 113CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
114CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 114CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
115 115
116ifdef NO_ICONV 116ifdef NO_ICONV
117 CFLAGS += -DNO_ICONV 117 CFLAGS += -DNO_ICONV
118endif 118endif
119ifdef NO_STRCASESTR 119ifdef NO_STRCASESTR
120 CFLAGS += -DNO_STRCASESTR 120 CFLAGS += -DNO_STRCASESTR
121endif 121endif
122 122
123cgit: $(OBJECTS) libgit 123cgit: $(OBJECTS) libgit
124 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS) 124 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
125 125
126cgit.o: VERSION 126cgit.o: VERSION
127 127
128-include $(OBJECTS:.o=.d) 128-include $(OBJECTS:.o=.d)
129 129
130libgit: 130libgit:
131 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) libgit.a 131 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 libgit.a
132 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) xdiff/lib.a 132 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 xdiff/lib.a
133 133
134test: all 134test: all
135 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all 135 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
136 136
137install: all 137install: all
138 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH) 138 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH)
139 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 139 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
140 install -m 0644 cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css 140 install -m 0644 cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css
141 install -m 0644 cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png 141 install -m 0644 cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png
142 142
143uninstall: 143uninstall:
144 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 144 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
145 rm -f $(CGIT_SCRIPT_PATH)/cgit.css 145 rm -f $(CGIT_SCRIPT_PATH)/cgit.css
146 rm -f $(CGIT_SCRIPT_PATH)/cgit.png 146 rm -f $(CGIT_SCRIPT_PATH)/cgit.png
147 147
148clean: 148clean:
149 rm -f cgit VERSION *.o *.d 149 rm -f cgit VERSION *.o *.d
150 150
151get-git: 151get-git:
152 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git 152 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 166fbc6..d1cee58 100644
--- a/cgit.c
+++ b/cgit.c
@@ -418,59 +418,60 @@ static int calc_ttl()
418} 418}
419 419
420int main(int argc, const char **argv) 420int main(int argc, const char **argv)
421{ 421{
422 const char *cgit_config_env = getenv("CGIT_CONFIG"); 422 const char *cgit_config_env = getenv("CGIT_CONFIG");
423 const char *path; 423 const char *path;
424 char *qry; 424 char *qry;
425 int err, ttl; 425 int err, ttl;
426 426
427 prepare_context(&ctx); 427 prepare_context(&ctx);
428 cgit_repolist.length = 0; 428 cgit_repolist.length = 0;
429 cgit_repolist.count = 0; 429 cgit_repolist.count = 0;
430 cgit_repolist.repos = NULL; 430 cgit_repolist.repos = NULL;
431 431
432 if (getenv("SCRIPT_NAME")) 432 if (getenv("SCRIPT_NAME"))
433 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); 433 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
434 if (getenv("QUERY_STRING")) 434 if (getenv("QUERY_STRING"))
435 ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); 435 ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
436 cgit_parse_args(argc, argv); 436 cgit_parse_args(argc, argv);
437 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 437 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
438 config_cb); 438 config_cb);
439 ctx.repo = NULL; 439 ctx.repo = NULL;
440 http_parse_querystring(ctx.qry.raw, querystring_cb); 440 http_parse_querystring(ctx.qry.raw, querystring_cb);
441 441
442 /* If virtual-root isn't specified in cgitrc and no url 442 /* If virtual-root isn't specified in cgitrc, lets pretend
443 * parameter is specified on the querystring, lets pretend 443 * that virtual-root equals SCRIPT_NAME.
444 * that virtualroot equals SCRIPT_NAME and use PATH_INFO as
445 * url. This allows cgit to work with virtual urls without
446 * the need for rewriterules in the webserver (as long as
447 * PATH_INFO is included in the cache lookup key).
448 */ 444 */
449 if (!ctx.cfg.virtual_root && !ctx.qry.url) { 445 if (!ctx.cfg.virtual_root)
450 ctx.cfg.virtual_root = ctx.cfg.script_name; 446 ctx.cfg.virtual_root = ctx.cfg.script_name;
451 path = getenv("PATH_INFO"); 447
452 if (path) { 448 /* If no url parameter is specified on the querystring, lets
453 if (path[0] == '/') 449 * use PATH_INFO as url. This allows cgit to work with virtual
454 path++; 450 * urls without the need for rewriterules in the webserver (as
455 ctx.qry.url = xstrdup(path); 451 * long as PATH_INFO is included in the cache lookup key).
456 if (ctx.qry.raw) { 452 */
457 qry = ctx.qry.raw; 453 path = getenv("PATH_INFO");
458 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 454 if (!ctx.qry.url && path) {
459 free(qry); 455 if (path[0] == '/')
460 } else 456 path++;
461 ctx.qry.raw = ctx.qry.url; 457 ctx.qry.url = xstrdup(path);
462 cgit_parse_url(ctx.qry.url); 458 if (ctx.qry.raw) {
463 } 459 qry = ctx.qry.raw;
460 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
461 free(qry);
462 } else
463 ctx.qry.raw = ctx.qry.url;
464 cgit_parse_url(ctx.qry.url);
464 } 465 }
465 466
466 ttl = calc_ttl(); 467 ttl = calc_ttl();
467 ctx.page.expires += ttl*60; 468 ctx.page.expires += ttl*60;
468 if (ctx.cfg.nocache) 469 if (ctx.cfg.nocache)
469 ctx.cfg.cache_size = 0; 470 ctx.cfg.cache_size = 0;
470 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 471 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
471 ctx.qry.raw, ttl, process_request, &ctx); 472 ctx.qry.raw, ttl, process_request, &ctx);
472 if (err) 473 if (err)
473 cgit_print_error(fmt("Error processing page: %s (%d)", 474 cgit_print_error(fmt("Error processing page: %s (%d)",
474 strerror(err), err)); 475 strerror(err), err));
475 return err; 476 return err;
476} 477}
diff --git a/cgit.css b/cgit.css
index 7928c2f..f19446d 100644
--- a/cgit.css
+++ b/cgit.css
@@ -450,24 +450,48 @@ span.age-hours {
450 color: #080; 450 color: #080;
451} 451}
452 452
453span.age-days { 453span.age-days {
454 color: #040; 454 color: #040;
455} 455}
456 456
457span.age-weeks { 457span.age-weeks {
458 color: #444; 458 color: #444;
459} 459}
460 460
461span.age-months { 461span.age-months {
462 color: #888; 462 color: #888;
463} 463}
464 464
465span.age-years { 465span.age-years {
466 color: #bbb; 466 color: #bbb;
467} 467}
468div.footer { 468div.footer {
469 margin-top: 0.5em; 469 margin-top: 0.5em;
470 text-align: center; 470 text-align: center;
471 font-size: 80%; 471 font-size: 80%;
472 color: #ccc; 472 color: #ccc;
473} 473}
474a.branch-deco {
475 margin: 0px 0.5em;
476 padding: 0px 0.25em;
477 background-color: #88ff88;
478 border: solid 1px #007700;
479}
480a.tag-deco {
481 margin: 0px 0.5em;
482 padding: 0px 0.25em;
483 background-color: #ffff88;
484 border: solid 1px #777700;
485}
486a.remote-deco {
487 margin: 0px 0.5em;
488 padding: 0px 0.25em;
489 background-color: #ccccff;
490 border: solid 1px #000077;
491}
492a.deco {
493 margin: 0px 0.5em;
494 padding: 0px 0.25em;
495 background-color: #ff8888;
496 border: solid 1px #770000;
497}
diff --git a/tests/t0104-tree.sh b/tests/t0104-tree.sh
index 0d62cc8..33f4eb0 100755
--- a/tests/t0104-tree.sh
+++ b/tests/t0104-tree.sh
@@ -1,33 +1,33 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./setup.sh 3. ./setup.sh
4 4
5prepare_tests "Check content on tree page" 5prepare_tests "Check content on tree page"
6 6
7run_test 'generate bar/tree' 'cgit_url "bar/tree" >trash/tmp' 7run_test 'generate bar/tree' 'cgit_url "bar/tree" >trash/tmp'
8run_test 'find file-1' 'grep -e "file-1" trash/tmp' 8run_test 'find file-1' 'grep -e "file-1" trash/tmp'
9run_test 'find file-50' 'grep -e "file-50" trash/tmp' 9run_test 'find file-50' 'grep -e "file-50" trash/tmp'
10 10
11run_test 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >trash/tmp' 11run_test 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >trash/tmp'
12 12
13run_test 'find line 1' ' 13run_test 'find line 1' '
14 grep -e "<a id=.n1. name=.n1. href=.#n1.>1</a>" trash/tmp 14 grep -e "<a id=.n1. name=.n1. href=.#n1.>1</a>" trash/tmp
15' 15'
16 16
17run_test 'no line 2' ' 17run_test 'no line 2' '
18 grep -e "<a id=.n2. name=.n2. href=.#n2.>2</a>" trash/tmp 18 ! grep -e "<a id=.n2. name=.n2. href=.#n2.>2</a>" trash/tmp
19' 19'
20 20
21run_test 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >trash/tmp' 21run_test 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >trash/tmp'
22 22
23run_test 'verify a+b link' ' 23run_test 'verify a+b link' '
24 grep -e "/foo+bar/tree/a+b" trash/tmp 24 grep -e "/foo+bar/tree/a+b" trash/tmp
25' 25'
26 26
27run_test 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >trash/tmp' 27run_test 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >trash/tmp'
28 28
29run_test 'verify a+b?h=1+2 link' ' 29run_test 'verify a+b?h=1+2 link' '
30 grep -e "/foo+bar/tree/a+b?h=1%2b2" trash/tmp 30 grep -e "/foo+bar/tree/a+b?h=1%2b2" trash/tmp
31' 31'
32 32
33tests_done 33tests_done
diff --git a/ui-log.c b/ui-log.c
index 2f90778..c3757dd 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -10,66 +10,99 @@
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13int files, add_lines, rem_lines; 13int files, add_lines, rem_lines;
14 14
15void count_lines(char *line, int size) 15void count_lines(char *line, int size)
16{ 16{
17 if (size <= 0) 17 if (size <= 0)
18 return; 18 return;
19 19
20 if (line[0] == '+') 20 if (line[0] == '+')
21 add_lines++; 21 add_lines++;
22 22
23 else if (line[0] == '-') 23 else if (line[0] == '-')
24 rem_lines++; 24 rem_lines++;
25} 25}
26 26
27void inspect_files(struct diff_filepair *pair) 27void inspect_files(struct diff_filepair *pair)
28{ 28{
29 files++; 29 files++;
30 if (ctx.repo->enable_log_linecount) 30 if (ctx.repo->enable_log_linecount)
31 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines); 31 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines);
32} 32}
33 33
34void show_commit_decorations(struct commit *commit)
35{
36 struct name_decoration *deco;
37 static char buf[1024];
38
39 buf[sizeof(buf) - 1] = 0;
40 deco = lookup_decoration(&name_decoration, &commit->object);
41 while (deco) {
42 if (!prefixcmp(deco->name, "refs/heads/")) {
43 strncpy(buf, deco->name + 11, sizeof(buf) - 1);
44 cgit_log_link(buf, NULL, "branch-deco", buf, NULL, NULL,
45 0, NULL, NULL, ctx.qry.showmsg);
46 }
47 else if (!prefixcmp(deco->name, "tag: refs/tags/")) {
48 strncpy(buf, deco->name + 15, sizeof(buf) - 1);
49 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
50 }
51 else if (!prefixcmp(deco->name, "refs/remotes/")) {
52 strncpy(buf, deco->name + 13, sizeof(buf) - 1);
53 cgit_log_link(buf, NULL, "remote-deco", NULL,
54 sha1_to_hex(commit->object.sha1), NULL,
55 0, NULL, NULL, ctx.qry.showmsg);
56 }
57 else {
58 strncpy(buf, deco->name, sizeof(buf) - 1);
59 cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
60 sha1_to_hex(commit->object.sha1));
61 }
62 deco = deco->next;
63 }
64}
65
34void print_commit(struct commit *commit) 66void print_commit(struct commit *commit)
35{ 67{
36 struct commitinfo *info; 68 struct commitinfo *info;
37 char *tmp; 69 char *tmp;
38 int cols = 2; 70 int cols = 2;
39 71
40 info = cgit_parse_commit(commit); 72 info = cgit_parse_commit(commit);
41 htmlf("<tr%s><td>", 73 htmlf("<tr%s><td>",
42 ctx.qry.showmsg ? " class='logheader'" : ""); 74 ctx.qry.showmsg ? " class='logheader'" : "");
43 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); 75 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
44 tmp = cgit_pageurl(ctx.repo->url, "commit", tmp); 76 tmp = cgit_pageurl(ctx.repo->url, "commit", tmp);
45 html_link_open(tmp, NULL, NULL); 77 html_link_open(tmp, NULL, NULL);
46 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 78 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
47 html_link_close(); 79 html_link_close();
48 htmlf("</td><td%s>", 80 htmlf("</td><td%s>",
49 ctx.qry.showmsg ? " class='logsubject'" : ""); 81 ctx.qry.showmsg ? " class='logsubject'" : "");
50 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, 82 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
51 sha1_to_hex(commit->object.sha1)); 83 sha1_to_hex(commit->object.sha1));
84 show_commit_decorations(commit);
52 html("</td><td>"); 85 html("</td><td>");
53 html_txt(info->author); 86 html_txt(info->author);
54 if (ctx.repo->enable_log_filecount) { 87 if (ctx.repo->enable_log_filecount) {
55 files = 0; 88 files = 0;
56 add_lines = 0; 89 add_lines = 0;
57 rem_lines = 0; 90 rem_lines = 0;
58 cgit_diff_commit(commit, inspect_files); 91 cgit_diff_commit(commit, inspect_files);
59 html("</td><td>"); 92 html("</td><td>");
60 htmlf("%d", files); 93 htmlf("%d", files);
61 if (ctx.repo->enable_log_linecount) { 94 if (ctx.repo->enable_log_linecount) {
62 html("</td><td>"); 95 html("</td><td>");
63 htmlf("-%d/+%d", rem_lines, add_lines); 96 htmlf("-%d/+%d", rem_lines, add_lines);
64 } 97 }
65 } 98 }
66 html("</td></tr>\n"); 99 html("</td></tr>\n");
67 if (ctx.qry.showmsg) { 100 if (ctx.qry.showmsg) {
68 if (ctx.repo->enable_log_filecount) { 101 if (ctx.repo->enable_log_filecount) {
69 cols++; 102 cols++;
70 if (ctx.repo->enable_log_linecount) 103 if (ctx.repo->enable_log_linecount)
71 cols++; 104 cols++;
72 } 105 }
73 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>", 106 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>",
74 cols); 107 cols);
75 html_txt(info->msg); 108 html_txt(info->msg);
@@ -98,48 +131,50 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
98 const char *argv[] = {NULL, NULL, NULL, NULL, NULL}; 131 const char *argv[] = {NULL, NULL, NULL, NULL, NULL};
99 int argc = 2; 132 int argc = 2;
100 int i, columns = 3; 133 int i, columns = 3;
101 134
102 if (!tip) 135 if (!tip)
103 tip = ctx.qry.head; 136 tip = ctx.qry.head;
104 137
105 argv[1] = disambiguate_ref(tip); 138 argv[1] = disambiguate_ref(tip);
106 139
107 if (grep && pattern && (!strcmp(grep, "grep") || 140 if (grep && pattern && (!strcmp(grep, "grep") ||
108 !strcmp(grep, "author") || 141 !strcmp(grep, "author") ||
109 !strcmp(grep, "committer"))) 142 !strcmp(grep, "committer")))
110 argv[argc++] = fmt("--%s=%s", grep, pattern); 143 argv[argc++] = fmt("--%s=%s", grep, pattern);
111 144
112 if (path) { 145 if (path) {
113 argv[argc++] = "--"; 146 argv[argc++] = "--";
114 argv[argc++] = path; 147 argv[argc++] = path;
115 } 148 }
116 init_revisions(&rev, NULL); 149 init_revisions(&rev, NULL);
117 rev.abbrev = DEFAULT_ABBREV; 150 rev.abbrev = DEFAULT_ABBREV;
118 rev.commit_format = CMIT_FMT_DEFAULT; 151 rev.commit_format = CMIT_FMT_DEFAULT;
119 rev.verbose_header = 1; 152 rev.verbose_header = 1;
120 rev.show_root_diff = 0; 153 rev.show_root_diff = 0;
121 setup_revisions(argc, argv, &rev, NULL); 154 setup_revisions(argc, argv, &rev, NULL);
155 load_ref_decorations();
156 rev.show_decorations = 1;
122 rev.grep_filter.regflags |= REG_ICASE; 157 rev.grep_filter.regflags |= REG_ICASE;
123 compile_grep_patterns(&rev.grep_filter); 158 compile_grep_patterns(&rev.grep_filter);
124 prepare_revision_walk(&rev); 159 prepare_revision_walk(&rev);
125 160
126 if (pager) 161 if (pager)
127 html("<table class='list nowrap'>"); 162 html("<table class='list nowrap'>");
128 163
129 html("<tr class='nohover'><th class='left'>Age</th>" 164 html("<tr class='nohover'><th class='left'>Age</th>"
130 "<th class='left'>Commit message"); 165 "<th class='left'>Commit message");
131 if (pager) { 166 if (pager) {
132 html(" ("); 167 html(" (");
133 cgit_log_link("toggle", NULL, NULL, ctx.qry.head, ctx.qry.sha1, 168 cgit_log_link("toggle", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
134 ctx.qry.path, ctx.qry.ofs, ctx.qry.grep, 169 ctx.qry.path, ctx.qry.ofs, ctx.qry.grep,
135 ctx.qry.search, ctx.qry.showmsg ? 0 : 1); 170 ctx.qry.search, ctx.qry.showmsg ? 0 : 1);
136 html(")"); 171 html(")");
137 } 172 }
138 html("</th><th class='left'>Author</th>"); 173 html("</th><th class='left'>Author</th>");
139 if (ctx.repo->enable_log_filecount) { 174 if (ctx.repo->enable_log_filecount) {
140 html("<th class='left'>Files</th>"); 175 html("<th class='left'>Files</th>");
141 columns++; 176 columns++;
142 if (ctx.repo->enable_log_linecount) { 177 if (ctx.repo->enable_log_linecount) {
143 html("<th class='left'>Lines</th>"); 178 html("<th class='left'>Lines</th>");
144 columns++; 179 columns++;
145 } 180 }
diff --git a/ui-patch.c b/ui-patch.c
index e60877d..1d77336 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -80,39 +80,39 @@ void cgit_print_patch(char *hex)
80 if (!hex) 80 if (!hex)
81 hex = ctx.qry.head; 81 hex = ctx.qry.head;
82 82
83 if (get_sha1(hex, sha1)) { 83 if (get_sha1(hex, sha1)) {
84 cgit_print_error(fmt("Bad object id: %s", hex)); 84 cgit_print_error(fmt("Bad object id: %s", hex));
85 return; 85 return;
86 } 86 }
87 commit = lookup_commit_reference(sha1); 87 commit = lookup_commit_reference(sha1);
88 if (!commit) { 88 if (!commit) {
89 cgit_print_error(fmt("Bad commit reference: %s", hex)); 89 cgit_print_error(fmt("Bad commit reference: %s", hex));
90 return; 90 return;
91 } 91 }
92 info = cgit_parse_commit(commit); 92 info = cgit_parse_commit(commit);
93 93
94 if (commit->parents && commit->parents->item) 94 if (commit->parents && commit->parents->item)
95 hashcpy(old_sha1, commit->parents->item->object.sha1); 95 hashcpy(old_sha1, commit->parents->item->object.sha1);
96 else 96 else
97 hashclr(old_sha1); 97 hashclr(old_sha1);
98 98
99 patchname = fmt("%s.patch", sha1_to_hex(sha1)); 99 patchname = fmt("%s.patch", sha1_to_hex(sha1));
100 ctx.page.mimetype = "text/plain"; 100 ctx.page.mimetype = "text/plain";
101 ctx.page.filename = patchname; 101 ctx.page.filename = patchname;
102 cgit_print_http_headers(&ctx); 102 cgit_print_http_headers(&ctx);
103 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1)); 103 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
104 htmlf("From: %s%s\n", info->author, info->author_email); 104 htmlf("From: %s %s\n", info->author, info->author_email);
105 html("Date: "); 105 html("Date: ");
106 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n", ctx.cfg.local_time); 106 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n", ctx.cfg.local_time);
107 htmlf("Subject: %s\n\n", info->subject); 107 htmlf("Subject: %s\n\n", info->subject);
108 if (info->msg && *info->msg) { 108 if (info->msg && *info->msg) {
109 htmlf("%s", info->msg); 109 htmlf("%s", info->msg);
110 if (info->msg[strlen(info->msg) - 1] != '\n') 110 if (info->msg[strlen(info->msg) - 1] != '\n')
111 html("\n"); 111 html("\n");
112 } 112 }
113 html("---\n"); 113 html("---\n");
114 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL); 114 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL);
115 html("--\n"); 115 html("--\n");
116 htmlf("cgit %s\n", CGIT_VERSION); 116 htmlf("cgit %s\n", CGIT_VERSION);
117 cgit_free_commitinfo(info); 117 cgit_free_commitinfo(info);
118} 118}
diff --git a/ui-refs.c b/ui-refs.c
index d61ee7c..c35e694 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -120,49 +120,49 @@ static int print_tag(struct refinfo *ref)
120 tag = (struct tag *)ref->object; 120 tag = (struct tag *)ref->object;
121 info = ref->tag; 121 info = ref->tag;
122 if (!tag || !info) 122 if (!tag || !info)
123 return 1; 123 return 1;
124 html("<tr><td>"); 124 html("<tr><td>");
125 cgit_tag_link(name, NULL, NULL, ctx.qry.head, name); 125 cgit_tag_link(name, NULL, NULL, ctx.qry.head, name);
126 html("</td><td>"); 126 html("</td><td>");
127 if (ctx.repo->snapshots && (tag->tagged->type == OBJ_COMMIT)) 127 if (ctx.repo->snapshots && (tag->tagged->type == OBJ_COMMIT))
128 print_tag_downloads(ctx.repo, name); 128 print_tag_downloads(ctx.repo, name);
129 else 129 else
130 cgit_object_link(tag->tagged); 130 cgit_object_link(tag->tagged);
131 html("</td><td>"); 131 html("</td><td>");
132 if (info->tagger) 132 if (info->tagger)
133 html(info->tagger); 133 html(info->tagger);
134 html("</td><td colspan='2'>"); 134 html("</td><td colspan='2'>");
135 if (info->tagger_date > 0) 135 if (info->tagger_date > 0)
136 cgit_print_age(info->tagger_date, -1, NULL); 136 cgit_print_age(info->tagger_date, -1, NULL);
137 html("</td></tr>\n"); 137 html("</td></tr>\n");
138 } else { 138 } else {
139 if (!header) 139 if (!header)
140 print_tag_header(); 140 print_tag_header();
141 html("<tr><td>"); 141 html("<tr><td>");
142 html_txt(name); 142 html_txt(name);
143 html("</td><td>"); 143 html("</td><td>");
144 if (ctx.repo->snapshots && (tag->tagged->type == OBJ_COMMIT)) 144 if (ctx.repo->snapshots && (ref->object->type == OBJ_COMMIT))
145 print_tag_downloads(ctx.repo, name); 145 print_tag_downloads(ctx.repo, name);
146 else 146 else
147 cgit_object_link(ref->object); 147 cgit_object_link(ref->object);
148 html("</td></tr>\n"); 148 html("</td></tr>\n");
149 } 149 }
150 return 0; 150 return 0;
151} 151}
152 152
153static void print_refs_link(char *path) 153static void print_refs_link(char *path)
154{ 154{
155 html("<tr class='nohover'><td colspan='4'>"); 155 html("<tr class='nohover'><td colspan='4'>");
156 cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path); 156 cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path);
157 html("</td></tr>"); 157 html("</td></tr>");
158} 158}
159 159
160void cgit_print_branches(int maxcount) 160void cgit_print_branches(int maxcount)
161{ 161{
162 struct reflist list; 162 struct reflist list;
163 int i; 163 int i;
164 164
165 html("<tr class='nohover'><th class='left'>Branch</th>" 165 html("<tr class='nohover'><th class='left'>Branch</th>"
166 "<th class='left'>Commit message</th>" 166 "<th class='left'>Commit message</th>"
167 "<th class='left'>Author</th>" 167 "<th class='left'>Author</th>"
168 "<th class='left' colspan='2'>Age</th></tr>\n"); 168 "<th class='left' colspan='2'>Age</th></tr>\n");
diff --git a/ui-tree.c b/ui-tree.c
index 051db7c..9876c99 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -33,50 +33,52 @@ static void print_object(const unsigned char *sha1, char *path)
33 cgit_print_error(fmt("Error reading object %s", 33 cgit_print_error(fmt("Error reading object %s",
34 sha1_to_hex(sha1))); 34 sha1_to_hex(sha1)));
35 return; 35 return;
36 } 36 }
37 37
38 html(" ("); 38 html(" (");
39 cgit_plain_link("plain", NULL, NULL, ctx.qry.head, 39 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
40 curr_rev, path); 40 curr_rev, path);
41 htmlf(")<br/>blob: %s", sha1_to_hex(sha1)); 41 htmlf(")<br/>blob: %s", sha1_to_hex(sha1));
42 42
43 html("<table summary='blob content' class='blob'>\n"); 43 html("<table summary='blob content' class='blob'>\n");
44 idx = 0; 44 idx = 0;
45 start = 0; 45 start = 0;
46 lineno = 0; 46 lineno = 0;
47 while(idx < size) { 47 while(idx < size) {
48 if (buf[idx] == '\n') { 48 if (buf[idx] == '\n') {
49 buf[idx] = '\0'; 49 buf[idx] = '\0';
50 htmlf(linefmt, ++lineno); 50 htmlf(linefmt, ++lineno);
51 html_txt(buf + start); 51 html_txt(buf + start);
52 html("</td></tr>\n"); 52 html("</td></tr>\n");
53 start = idx + 1; 53 start = idx + 1;
54 } 54 }
55 idx++; 55 idx++;
56 } 56 }
57 htmlf(linefmt, ++lineno); 57 if (start < idx) {
58 html_txt(buf + start); 58 htmlf(linefmt, ++lineno);
59 html_txt(buf + start);
60 }
59 html("</td></tr>\n"); 61 html("</td></tr>\n");
60 html("</table>\n"); 62 html("</table>\n");
61} 63}
62 64
63 65
64static int ls_item(const unsigned char *sha1, const char *base, int baselen, 66static int ls_item(const unsigned char *sha1, const char *base, int baselen,
65 const char *pathname, unsigned int mode, int stage, 67 const char *pathname, unsigned int mode, int stage,
66 void *cbdata) 68 void *cbdata)
67{ 69{
68 char *name; 70 char *name;
69 char *fullpath; 71 char *fullpath;
70 enum object_type type; 72 enum object_type type;
71 unsigned long size = 0; 73 unsigned long size = 0;
72 74
73 name = xstrdup(pathname); 75 name = xstrdup(pathname);
74 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 76 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
75 ctx.qry.path ? "/" : "", name); 77 ctx.qry.path ? "/" : "", name);
76 78
77 if (!S_ISGITLINK(mode)) { 79 if (!S_ISGITLINK(mode)) {
78 type = sha1_object_info(sha1, &size); 80 type = sha1_object_info(sha1, &size);
79 if (type == OBJ_BAD) { 81 if (type == OBJ_BAD) {
80 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 82 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
81 name, 83 name,
82 sha1_to_hex(sha1)); 84 sha1_to_hex(sha1));