summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2007-05-13 20:25:14 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2007-05-13 20:31:11 (UTC)
commit8a3685bcf2612206fc24a2421acb53dd83aeab85 (patch) (unidiff)
tree4628d87e55e87ead2e097cdacf8b4160cd0fc118
parentc6cf3a424a0860d69b290254d9b19d35527b2d27 (diff)
downloadcgit-8a3685bcf2612206fc24a2421acb53dd83aeab85.zip
cgit-8a3685bcf2612206fc24a2421acb53dd83aeab85.tar.gz
cgit-8a3685bcf2612206fc24a2421acb53dd83aeab85.tar.bz2
Add graphical diffstat to commit view
The diffstat is calculated against the leftmost parent of the commit. This gives nice information for "normal" merges while octopus merges are less than optimal, so the diffstat isn't calculated for those merges. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile1
-rw-r--r--add.pngbin0 -> 168 bytes
-rw-r--r--cgit.css30
-rw-r--r--del.pngbin0 -> 168 bytes
-rw-r--r--ui-commit.c132
5 files changed, 130 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index ccc7582..ea4d818 100644
--- a/Makefile
+++ b/Makefile
@@ -1,79 +1,80 @@
1CGIT_VERSION = 0.3 1CGIT_VERSION = 0.3
2 2
3prefix = /var/www/htdocs/cgit 3prefix = /var/www/htdocs/cgit
4 4
5SHA1_HEADER = <openssl/sha.h> 5SHA1_HEADER = <openssl/sha.h>
6 6
7CACHE_ROOT = /var/cache/cgit 7CACHE_ROOT = /var/cache/cgit
8EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 8EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
9OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 9OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
10 ui-summary.o ui-log.o ui-view.o ui-tree.o ui-commit.o ui-diff.o \ 10 ui-summary.o ui-log.o ui-view.o ui-tree.o ui-commit.o ui-diff.o \
11 ui-snapshot.o ui-blob.o 11 ui-snapshot.o ui-blob.o
12 12
13CFLAGS += -Wall 13CFLAGS += -Wall
14 14
15ifdef DEBUG 15ifdef DEBUG
16 CFLAGS += -g 16 CFLAGS += -g
17endif 17endif
18 18
19CFLAGS += -Igit -DSHA1_HEADER='$(SHA1_HEADER)' 19CFLAGS += -Igit -DSHA1_HEADER='$(SHA1_HEADER)'
20 20
21 21
22# 22#
23# If make is run on a nongit platform, we need to get the git sources as a tarball. 23# If make is run on a nongit platform, we need to get the git sources as a tarball.
24# But there is currently no recent enough tarball available on kernel.org, so download 24# But there is currently no recent enough tarball available on kernel.org, so download
25# a zipfile from hjemli.net instead 25# a zipfile from hjemli.net instead
26# 26#
27GITVER = $(shell git version 2>/dev/null || echo nogit) 27GITVER = $(shell git version 2>/dev/null || echo nogit)
28ifeq ($(GITVER),nogit) 28ifeq ($(GITVER),nogit)
29GITURL = http://hjemli.net/git/git/snapshot/?id=v1.5.2-rc2 29GITURL = http://hjemli.net/git/git/snapshot/?id=v1.5.2-rc2
30INITGIT = test -e git/git.c || (curl "$(GITURL)" > tmp.zip && unzip tmp.zip) 30INITGIT = test -e git/git.c || (curl "$(GITURL)" > tmp.zip && unzip tmp.zip)
31else 31else
32INITGIT = ./submodules.sh -i 32INITGIT = ./submodules.sh -i
33endif 33endif
34 34
35 35
36# 36#
37# basic build rules 37# basic build rules
38# 38#
39all: cgit 39all: cgit
40 40
41cgit: cgit.c cgit.h $(OBJECTS) 41cgit: cgit.c cgit.h $(OBJECTS)
42 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \ 42 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \
43 $(OBJECTS) $(EXTLIBS) 43 $(OBJECTS) $(EXTLIBS)
44 44
45$(OBJECTS): cgit.h git/libgit.a 45$(OBJECTS): cgit.h git/libgit.a
46 46
47git/libgit.a: 47git/libgit.a:
48 $(INITGIT) 48 $(INITGIT)
49 $(MAKE) -C git 49 $(MAKE) -C git
50 50
51# 51#
52# phony targets 52# phony targets
53# 53#
54install: all clean-cache 54install: all clean-cache
55 mkdir -p $(prefix) 55 mkdir -p $(prefix)
56 install cgit $(prefix)/cgit.cgi 56 install cgit $(prefix)/cgit.cgi
57 install cgit.css $(prefix)/cgit.css 57 install cgit.css $(prefix)/cgit.css
58 install add.png del.png $(prefix)/
58 59
59clean-cgit: 60clean-cgit:
60 rm -f cgit *.o 61 rm -f cgit *.o
61 62
62distclean-cgit: clean-cgit 63distclean-cgit: clean-cgit
63 git clean -d -x 64 git clean -d -x
64 65
65clean-sub: 66clean-sub:
66 $(MAKE) -C git clean 67 $(MAKE) -C git clean
67 68
68distclean-sub: clean-sub 69distclean-sub: clean-sub
69 $(shell cd git && git clean -d -x) 70 $(shell cd git && git clean -d -x)
70 71
71clean-cache: 72clean-cache:
72 rm -rf $(CACHE_ROOT)/* 73 rm -rf $(CACHE_ROOT)/*
73 74
74clean: clean-cgit clean-sub 75clean: clean-cgit clean-sub
75 76
76distclean: distclean-cgit distclean-sub 77distclean: distclean-cgit distclean-sub
77 78
78.PHONY: all install clean clean-cgit clean-sub clean-cache \ 79.PHONY: all install clean clean-cgit clean-sub clean-cache \
79 distclean distclean-cgit distclean-sub 80 distclean distclean-cgit distclean-sub
diff --git a/add.png b/add.png
new file mode 100644
index 0000000..c550388
--- a/dev/null
+++ b/add.png
Binary files differ
diff --git a/cgit.css b/cgit.css
index cded981..b736b19 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,296 +1,322 @@
1body { 1body {
2 font-family: arial; 2 font-family: arial;
3 font-size: 11pt; 3 font-size: 11pt;
4 background: white; 4 background: white;
5} 5}
6 6
7body, table { 7body, table {
8 padding: 0em; 8 padding: 0em;
9 margin: 0em; 9 margin: 0em;
10} 10}
11 11
12table { 12table {
13 border-collapse: collapse; 13 border-collapse: collapse;
14} 14}
15 15
16h2 { 16h2 {
17 font-size: 120%; 17 font-size: 120%;
18 font-weight: bold; 18 font-weight: bold;
19 margin-top: 0em; 19 margin-top: 0em;
20 margin-bottom: 0.25em; 20 margin-bottom: 0.25em;
21} 21}
22 22
23h3 { 23h3 {
24 margin-top: 0em; 24 margin-top: 0em;
25 font-size: 100%; 25 font-size: 100%;
26 font-weight: normal; 26 font-weight: normal;
27} 27}
28 28
29h4 {
30 margin-top: 1.5em;
31 margin-bottom: 0.1em;
32 font-size: 100%;
33 font-weight: bold;
34}
35
29a { 36a {
30 color: blue; 37 color: blue;
31 text-decoration: none; 38 text-decoration: none;
32} 39}
33 40
34a:hover { 41a:hover {
35 text-decoration: underline; 42 text-decoration: underline;
36} 43}
37 44
38table.list { 45table.list {
39 border: none; 46 border: none;
40 border-collapse: collapse; 47 border-collapse: collapse;
41} 48}
42 49
43table.list tr { 50table.list tr {
44 background: white; 51 background: white;
45} 52}
46 53
47table.list tr:hover { 54table.list tr:hover {
48 background: #eee; 55 background: #eee;
49} 56}
50 57
51table.list tr.nohover:hover { 58table.list tr.nohover:hover {
52 background: white; 59 background: white;
53} 60}
54 61
55table.list th { 62table.list th {
56 font-weight: normal; 63 font-weight: normal;
57 border-bottom: solid 1px #777; 64 border-bottom: solid 1px #777;
58 padding: 0.1em 0.5em 0.1em 0.5em; 65 padding: 0.1em 0.5em 0.1em 0.5em;
59 vertical-align: baseline; 66 vertical-align: baseline;
60} 67}
61 68
62table.list td { 69table.list td {
63 border: none; 70 border: none;
64 padding: 0.1em 0.5em 0.1em 0.5em; 71 padding: 0.1em 0.5em 0.1em 0.5em;
65} 72}
66 73
67img { 74img {
68 border: none; 75 border: none;
69} 76}
70 77
71table#layout { 78table#layout {
72 width: 100%; 79 width: 100%;
73 border-collapse: collapse; 80 border-collapse: collapse;
74 margin: 0px; 81 margin: 0px;
75} 82}
76 83
77td#header, td#logo { 84td#header, td#logo {
78 color: #666; 85 color: #666;
79 background-color: #ddd; 86 background-color: #ddd;
80 border-bottom: solid 1px #000; 87 border-bottom: solid 1px #000;
81} 88}
82 89
83td#header { 90td#header {
84 font-size: 150%; 91 font-size: 150%;
85 font-weight: bold; 92 font-weight: bold;
86 padding: 0.2em 0.5em; 93 padding: 0.2em 0.5em;
87 vertical-align: text-bottom; 94 vertical-align: text-bottom;
88} 95}
89 96
90td#logo { 97td#logo {
91 text-align: right; 98 text-align: right;
92 vertical-align: middle; 99 vertical-align: middle;
93 padding-right: 0.5em; 100 padding-right: 0.5em;
94} 101}
95 102
96td#crumb, td#search { 103td#crumb, td#search {
97 color: #ccc; 104 color: #ccc;
98 border-top: solid 3px #555; 105 border-top: solid 3px #555;
99 background-color: #666; 106 background-color: #666;
100 border-bottom: solid 1px #333; 107 border-bottom: solid 1px #333;
101 padding: 2px 1em; 108 padding: 2px 1em;
102} 109}
103 110
104td#crumb { 111td#crumb {
105 font-weight: bold; 112 font-weight: bold;
106} 113}
107 114
108td#crumb a { 115td#crumb a {
109 color: #ccc; 116 color: #ccc;
110} 117}
111 118
112td#crumb a:hover { 119td#crumb a:hover {
113 color: #eee; 120 color: #eee;
114} 121}
115 122
116td#search { 123td#search {
117 text-align: right; 124 text-align: right;
118 vertical-align: center; 125 vertical-align: center;
119 padding-right: 0.5em; 126 padding-right: 0.5em;
120} 127}
121 128
122td#search form { 129td#search form {
123 margin: 0px; 130 margin: 0px;
124 padding: 0px; 131 padding: 0px;
125} 132}
126 133
127td#search input { 134td#search input {
128 font-size: 9pt; 135 font-size: 9pt;
129 padding: 0px; 136 padding: 0px;
130 width: 10em; 137 width: 10em;
131 border: solid 1px #333; 138 border: solid 1px #333;
132 color: #333; 139 color: #333;
133 background-color: #fff; 140 background-color: #fff;
134} 141}
135 142
136td#summary { 143td#summary {
137 vertical-align: top; 144 vertical-align: top;
138 padding-bottom: 1em; 145 padding-bottom: 1em;
139} 146}
140 147
141td#archivelist { 148td#archivelist {
142 padding-bottom: 1em; 149 padding-bottom: 1em;
143} 150}
144 151
145td#archivelist table { 152td#archivelist table {
146 float: right; 153 float: right;
147 border-collapse: collapse; 154 border-collapse: collapse;
148 border: solid 1px #777; 155 border: solid 1px #777;
149} 156}
150 157
151td#archivelist table th { 158td#archivelist table th {
152 background-color: #ccc; 159 background-color: #ccc;
153} 160}
154 161
155td#content { 162td#content {
156 padding: 1em 0.5em; 163 padding: 1em 0.5em;
157} 164}
158 165
159div#blob { 166div#blob {
160 border: solid 1px black; 167 border: solid 1px black;
161} 168}
162 169
163div.error { 170div.error {
164 color: red; 171 color: red;
165 font-weight: bold; 172 font-weight: bold;
166 margin: 1em 2em; 173 margin: 1em 2em;
167} 174}
168 175
169td.ls-blob, td.ls-dir, td.ls-mod { 176td.ls-blob, td.ls-dir, td.ls-mod {
170 font-family: monospace; 177 font-family: monospace;
171} 178}
172 179
173div.ls-dir a { 180div.ls-dir a {
174 font-weight: bold; 181 font-weight: bold;
175} 182}
176 183
177th.filesize, td.filesize { 184th.filesize, td.filesize {
178 text-align: right; 185 text-align: right;
179} 186}
180 187
181td.filesize { 188td.filesize {
182 font-family: monospace; 189 font-family: monospace;
183} 190}
184 191
185td.filemode { 192td.filemode {
186 font-family: monospace; 193 font-family: monospace;
187} 194}
188 195
189td.blob { 196td.blob {
190 white-space: pre; 197 white-space: pre;
191 font-family: monospace; 198 font-family: monospace;
192 background-color: white; 199 background-color: white;
193} 200}
194 201
195table.nowrap td { 202table.nowrap td {
196 white-space: nowrap; 203 white-space: nowrap;
197} 204}
198 205
199table.commit-info { 206table.commit-info {
200 border-collapse: collapse; 207 border-collapse: collapse;
201 margin-top: 1.5em; 208 margin-top: 1.5em;
202} 209}
203 210
204table.commit-info th { 211table.commit-info th {
205 text-align: left; 212 text-align: left;
206 font-weight: normal; 213 font-weight: normal;
207 padding: 0.1em 1em 0.1em 0.1em; 214 padding: 0.1em 1em 0.1em 0.1em;
208} 215}
209 216
210table.commit-info td { 217table.commit-info td {
211 font-weight: normal; 218 font-weight: normal;
212 padding: 0.1em 1em 0.1em 0.1em; 219 padding: 0.1em 1em 0.1em 0.1em;
213} 220}
214 221
215div.commit-subject { 222div.commit-subject {
216 font-weight: bold; 223 font-weight: bold;
217 font-size: 125%; 224 font-size: 125%;
218 margin: 1.5em 0em 0.5em 0em; 225 margin: 1.5em 0em 0.5em 0em;
219 padding: 0em; 226 padding: 0em;
220} 227}
221 228
222div.commit-msg { 229div.commit-msg {
223 white-space: pre; 230 white-space: pre;
224 font-family: monospace; 231 font-family: monospace;
225} 232}
226 233
227table.diffstat { 234table.diffstat {
228 border-collapse: collapse; 235 border-collapse: collapse;
229 margin-top: 1.5em; 236 margin-top: 1.5em;
237 width: 100%;
238 border: solid 1px #aaa;
239}
240
241table.diffstat tr:hover {
242 background-color: #eee;
230} 243}
231 244
232table.diffstat th { 245table.diffstat th {
233 font-weight: normal; 246 font-weight: normal;
234 text-align: left; 247 text-align: left;
235 text-decoration: underline; 248 text-decoration: underline;
236 padding: 0.1em 1em 0.1em 0.1em; 249 padding: 0.1em 1em 0.1em 0.1em;
237 font-size: 100%; 250 font-size: 100%;
238} 251}
239 252
240table.diffstat td { 253table.diffstat td {
241 padding: 0.1em 1em 0.1em 0.1em; 254 padding: 0.2em 0.2em 0.1em 0.1em;
242 font-size: 100%; 255 font-size: 100%;
256 border: none;
257 border-top: solid 1px #aaa;
258 border-bottom: solid 1px #aaa;
243} 259}
244 260
245table.diffstat td span.modechange { 261table.diffstat td span.modechange {
246 padding-left: 1em; 262 padding-left: 1em;
247 color: red; 263 color: red;
248} 264}
249 265
250table.diffstat td.add a { 266table.diffstat td.add a {
251 color: green; 267 color: green;
252} 268}
253 269
254table.diffstat td.del a { 270table.diffstat td.del a {
255 color: red; 271 color: red;
256} 272}
257 273
258table.diffstat td.upd a { 274table.diffstat td.upd a {
259 color: blue; 275 color: blue;
260} 276}
261 277
262table.diffstat td.summary { 278table.diffstat td.graph {
279 width: 75%;
280 vertical-align: center;
281}
282
283table.diffstat td.graph img {
284 border: none;
285 height: 11pt;
286}
287
288div.diffstat-summary {
263 color: #888; 289 color: #888;
264 padding-top: 0.5em; 290 padding-top: 0.5em;
265} 291}
266 292
267table.diff td { 293table.diff td {
268 border: solid 1px black; 294 border: solid 1px black;
269 font-family: monospace; 295 font-family: monospace;
270 white-space: pre; 296 white-space: pre;
271} 297}
272 298
273table.diff td div.hunk { 299table.diff td div.hunk {
274 background: #ccc; 300 background: #ccc;
275} 301}
276 302
277table.diff td div.add { 303table.diff td div.add {
278 color: green; 304 color: green;
279} 305}
280 306
281table.diff td div.del { 307table.diff td div.del {
282 color: red; 308 color: red;
283} 309}
284 310
285.sha1 { 311.sha1 {
286 font-family: courier; 312 font-family: courier;
287 font-size: 90%; 313 font-size: 90%;
288} 314}
289 315
290.left { 316.left {
291 text-align: left; 317 text-align: left;
292} 318}
293 319
294.right { 320.right {
295 text-align: right; 321 text-align: right;
296} 322}
diff --git a/del.png b/del.png
new file mode 100644
index 0000000..5c73e82
--- a/dev/null
+++ b/del.png
Binary files differ
diff --git a/ui-commit.c b/ui-commit.c
index f1a22d3..ce33cf9 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,158 +1,228 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11int files = 0; 11int files = 0, slots = 0;
12int total_adds = 0, total_rems = 0, max_changes = 0;
13int lines_added, lines_removed;
12 14
13void print_filepair(struct diff_filepair *pair) 15struct fileinfo {
16 char status;
17 unsigned char old_sha1[20];
18 unsigned char new_sha1[20];
19 unsigned short old_mode;
20 unsigned short new_mode;
21 char *old_path;
22 char *new_path;
23 unsigned int added;
24 unsigned int removed;
25} *items;
26
27
28void print_fileinfo(struct fileinfo *info)
14{ 29{
15 char *query; 30 char *query, *query2;
16 char *class; 31 char *class;
32 double width;
17 33
18 switch (pair->status) { 34 switch (info->status) {
19 case DIFF_STATUS_ADDED: 35 case DIFF_STATUS_ADDED:
20 class = "add"; 36 class = "add";
21 break; 37 break;
22 case DIFF_STATUS_COPIED: 38 case DIFF_STATUS_COPIED:
23 class = "cpy"; 39 class = "cpy";
24 break; 40 break;
25 case DIFF_STATUS_DELETED: 41 case DIFF_STATUS_DELETED:
26 class = "del"; 42 class = "del";
27 break; 43 break;
28 case DIFF_STATUS_MODIFIED: 44 case DIFF_STATUS_MODIFIED:
29 class = "upd"; 45 class = "upd";
30 break; 46 break;
31 case DIFF_STATUS_RENAMED: 47 case DIFF_STATUS_RENAMED:
32 class = "mov"; 48 class = "mov";
33 break; 49 break;
34 case DIFF_STATUS_TYPE_CHANGED: 50 case DIFF_STATUS_TYPE_CHANGED:
35 class = "typ"; 51 class = "typ";
36 break; 52 break;
37 case DIFF_STATUS_UNKNOWN: 53 case DIFF_STATUS_UNKNOWN:
38 class = "unk"; 54 class = "unk";
39 break; 55 break;
40 case DIFF_STATUS_UNMERGED: 56 case DIFF_STATUS_UNMERGED:
41 class = "stg"; 57 class = "stg";
42 break; 58 break;
43 default: 59 default:
44 die("bug: unhandled diff status %c", pair->status); 60 die("bug: unhandled diff status %c", info->status);
45 } 61 }
46 62
47 html("<tr>"); 63 html("<tr>");
48 htmlf("<td class='mode'>"); 64 htmlf("<td class='mode'>");
49 if (is_null_sha1(pair->two->sha1)) { 65 if (is_null_sha1(info->new_sha1)) {
50 html_filemode(pair->one->mode); 66 html_filemode(info->old_mode);
51 } else { 67 } else {
52 html_filemode(pair->two->mode); 68 html_filemode(info->new_mode);
53 } 69 }
54 70
55 if (pair->one->mode != pair->two->mode && 71 if (info->old_mode != info->new_mode &&
56 !is_null_sha1(pair->one->sha1) && 72 !is_null_sha1(info->old_sha1) &&
57 !is_null_sha1(pair->two->sha1)) { 73 !is_null_sha1(info->new_sha1)) {
58 html("<span class='modechange'>["); 74 html("<span class='modechange'>[");
59 html_filemode(pair->one->mode); 75 html_filemode(info->old_mode);
60 html("]</span>"); 76 html("]</span>");
61 } 77 }
62 htmlf("</td><td class='%s'>", class); 78 htmlf("</td><td class='%s'>", class);
63 query = fmt("id=%s&id2=%s", sha1_to_hex(pair->one->sha1), 79 query = fmt("id=%s&id2=%s", sha1_to_hex(info->old_sha1),
64 sha1_to_hex(pair->two->sha1)); 80 sha1_to_hex(info->new_sha1));
65 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), 81 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query),
66 NULL, NULL); 82 NULL, NULL);
67 if (pair->status == DIFF_STATUS_COPIED || 83 if (info->status == DIFF_STATUS_COPIED ||
68 pair->status == DIFF_STATUS_RENAMED) { 84 info->status == DIFF_STATUS_RENAMED) {
69 html_txt(pair->two->path); 85 html_txt(info->new_path);
70 htmlf("</a> (%s from ", pair->status == DIFF_STATUS_COPIED ? 86 htmlf("</a> (%s from ", info->status == DIFF_STATUS_COPIED ?
71 "copied" : "renamed"); 87 "copied" : "renamed");
72 query = fmt("id=%s", sha1_to_hex(pair->one->sha1)); 88 query2 = fmt("id=%s", sha1_to_hex(info->old_sha1));
73 html_link_open(cgit_pageurl(cgit_query_repo, "view", query), 89 html_link_open(cgit_pageurl(cgit_query_repo, "view", query2),
74 NULL, NULL); 90 NULL, NULL);
75 html_txt(pair->one->path); 91 html_txt(info->old_path);
76 html("</a>)"); 92 html("</a>)");
77 } else { 93 } else {
78 html_txt(pair->two->path); 94 html_txt(info->new_path);
79 html("</a>"); 95 html("</a>");
80 } 96 }
81 html("<td>"); 97 html("</td><td class='right'>");
98 htmlf("%d", info->added + info->removed);
82 99
83 //TODO: diffstat graph 100 html("</td><td class='graph'>");
101 width = (info->added + info->removed) * 100.0 / max_changes;
102 if (width < 0.1)
103 width = 0.1;
104 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query),
105 NULL, NULL);
106 htmlf("<img src='/cgit/add.png' style='width: %.1f%%;'/>",
107 info->added * width / (info->added + info->removed));
108 htmlf("<img src='/cgit/del.png' style='width: %.1f%%;'/>",
109 info->removed * width / (info->added + info->removed));
110 html("</a></td></tr>\n");
111}
84 112
85 html("</td></tr>\n"); 113void cgit_count_diff_lines(char *line, int len)
114{
115 if (line && (len > 0)) {
116 if (line[0] == '+')
117 lines_added++;
118 else if (line[0] == '-')
119 lines_removed++;
120 }
121}
122
123void inspect_filepair(struct diff_filepair *pair)
124{
86 files++; 125 files++;
126 lines_added = 0;
127 lines_removed = 0;
128 cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines);
129 if (files >= slots) {
130 if (slots == 0)
131 slots = 4;
132 else
133 slots = slots * 2;
134 items = xrealloc(items, slots * sizeof(struct fileinfo));
135 }
136 items[files-1].status = pair->status;
137 hashcpy(items[files-1].old_sha1, pair->one->sha1);
138 hashcpy(items[files-1].new_sha1, pair->two->sha1);
139 items[files-1].old_mode = pair->one->mode;
140 items[files-1].new_mode = pair->two->mode;
141 items[files-1].old_path = xstrdup(pair->one->path);
142 items[files-1].new_path = xstrdup(pair->two->path);
143 items[files-1].added = lines_added;
144 items[files-1].removed = lines_removed;
145 if (lines_added + lines_removed > max_changes)
146 max_changes = lines_added + lines_removed;
147 total_adds += lines_added;
148 total_rems += lines_removed;
87} 149}
88 150
151
89void cgit_print_commit(const char *hex) 152void cgit_print_commit(const char *hex)
90{ 153{
91 struct commit *commit; 154 struct commit *commit;
92 struct commitinfo *info; 155 struct commitinfo *info;
93 struct commit_list *p; 156 struct commit_list *p;
94 unsigned char sha1[20]; 157 unsigned char sha1[20];
95 char *query; 158 char *query;
96 char *filename; 159 char *filename;
160 int i;
97 161
98 if (get_sha1(hex, sha1)) { 162 if (get_sha1(hex, sha1)) {
99 cgit_print_error(fmt("Bad object id: %s", hex)); 163 cgit_print_error(fmt("Bad object id: %s", hex));
100 return; 164 return;
101 } 165 }
102 commit = lookup_commit_reference(sha1); 166 commit = lookup_commit_reference(sha1);
103 if (!commit) { 167 if (!commit) {
104 cgit_print_error(fmt("Bad commit reference: %s", hex)); 168 cgit_print_error(fmt("Bad commit reference: %s", hex));
105 return; 169 return;
106 } 170 }
107 info = cgit_parse_commit(commit); 171 info = cgit_parse_commit(commit);
108 172
109 html("<table class='commit-info'>\n"); 173 html("<table class='commit-info'>\n");
110 html("<tr><th>author</th><td>"); 174 html("<tr><th>author</th><td>");
111 html_txt(info->author); 175 html_txt(info->author);
112 html(" "); 176 html(" ");
113 html_txt(info->author_email); 177 html_txt(info->author_email);
114 html("</td><td class='right'>"); 178 html("</td><td class='right'>");
115 cgit_print_date(info->author_date); 179 cgit_print_date(info->author_date);
116 html("</td></tr>\n"); 180 html("</td></tr>\n");
117 html("<tr><th>committer</th><td>"); 181 html("<tr><th>committer</th><td>");
118 html_txt(info->committer); 182 html_txt(info->committer);
119 html(" "); 183 html(" ");
120 html_txt(info->committer_email); 184 html_txt(info->committer_email);
121 html("</td><td class='right'>"); 185 html("</td><td class='right'>");
122 cgit_print_date(info->committer_date); 186 cgit_print_date(info->committer_date);
123 html("</td></tr>\n"); 187 html("</td></tr>\n");
124 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='"); 188 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='");
125 query = fmt("id=%s", sha1_to_hex(commit->tree->object.sha1)); 189 query = fmt("id=%s", sha1_to_hex(commit->tree->object.sha1));
126 html_attr(cgit_pageurl(cgit_query_repo, "tree", query)); 190 html_attr(cgit_pageurl(cgit_query_repo, "tree", query));
127 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1)); 191 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1));
128 for (p = commit->parents; p ; p = p->next) { 192 for (p = commit->parents; p ; p = p->next) {
129 html("<tr><th>parent</th>" 193 html("<tr><th>parent</th>"
130 "<td colspan='2' class='sha1'>" 194 "<td colspan='2' class='sha1'>"
131 "<a href='"); 195 "<a href='");
132 query = fmt("id=%s", sha1_to_hex(p->item->object.sha1)); 196 query = fmt("id=%s", sha1_to_hex(p->item->object.sha1));
133 html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); 197 html_attr(cgit_pageurl(cgit_query_repo, "commit", query));
134 htmlf("'>%s</a></td></tr>\n", 198 htmlf("'>%s</a></td></tr>\n",
135 sha1_to_hex(p->item->object.sha1)); 199 sha1_to_hex(p->item->object.sha1));
136 } 200 }
137 if (cgit_repo->snapshots) { 201 if (cgit_repo->snapshots) {
138 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='"); 202 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='");
139 filename = fmt("%s-%s.zip", cgit_query_repo, hex); 203 filename = fmt("%s-%s.zip", cgit_query_repo, hex);
140 html_attr(cgit_pageurl(cgit_query_repo, "snapshot", 204 html_attr(cgit_pageurl(cgit_query_repo, "snapshot",
141 fmt("id=%s&name=%s", hex, filename))); 205 fmt("id=%s&name=%s", hex, filename)));
142 htmlf("'>%s</a></td></tr>", filename); 206 htmlf("'>%s</a></td></tr>", filename);
143 } 207 }
144 html("</table>\n"); 208 html("</table>\n");
145 html("<div class='commit-subject'>"); 209 html("<div class='commit-subject'>");
146 html_txt(info->subject); 210 html_txt(info->subject);
147 html("</div>"); 211 html("</div>");
148 html("<div class='commit-msg'>"); 212 html("<div class='commit-msg'>");
149 html_txt(info->msg); 213 html_txt(info->msg);
150 html("</div>"); 214 html("</div>");
151 html("<table class='diffstat'>"); 215 if (!(commit->parents && commit->parents->next && commit->parents->next->next)) {
152 html("<tr><th colspan='3'>Affected files</tr>\n"); 216 html("<table class='diffstat'>");
153 cgit_diff_commit(commit, print_filepair); 217 max_changes = 0;
154 htmlf("<tr><td colspan='3' class='summary'>" 218 cgit_diff_commit(commit, inspect_filepair);
155 "%d file%s changed</td></tr>\n", files, files > 1 ? "s" : ""); 219 for(i = 0; i<files; i++)
156 html("</table>"); 220 print_fileinfo(&items[i]);
221 html("</table>");
222 html("<div class='diffstat-summary'>");
223 htmlf("%d files changed, %d insertions, %d deletions\n",
224 files, total_adds, total_rems);
225 html("</div>");
226 }
157 cgit_free_commitinfo(info); 227 cgit_free_commitinfo(info);
158} 228}