author | Ragnar Ouchterlony <ragnar@lysator.liu.se> | 2009-09-16 16:56:26 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2009-09-16 18:17:56 (UTC) |
commit | 4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9 (patch) (unidiff) | |
tree | c6389de8ae75cf907ed3884e87ee554d80498145 | |
parent | 207cc34711039329b41345f716bf421a88a6fd0a (diff) | |
download | cgit-4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9.zip cgit-4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9.tar.gz cgit-4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9.tar.bz2 |
Fixed side-by-side diff bugs related to binary diff and more.
The fixed bugs:
* "Binary files differ" did not show up either in unidiff or
side-by-side-diff.
* Subproject diffs did not work for side-by-side diffs.
* The ssdiff link on diff pages did not conserve the path.
Signed-off-by: Ragnar Ouchterlony <ragnar@lysator.liu.se>
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
-rw-r--r-- | ui-diff.c | 14 | ||||
-rw-r--r-- | ui-ssdiff.c | 8 |
2 files changed, 16 insertions, 6 deletions
@@ -1,347 +1,353 @@ | |||
1 | /* ui-diff.c: show diff between two blobs | 1 | /* ui-diff.c: show diff between two blobs |
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 | #include "html.h" | 10 | #include "html.h" |
11 | #include "ui-shared.h" | 11 | #include "ui-shared.h" |
12 | #include "ui-ssdiff.h" | 12 | #include "ui-ssdiff.h" |
13 | 13 | ||
14 | unsigned char old_rev_sha1[20]; | 14 | unsigned char old_rev_sha1[20]; |
15 | unsigned char new_rev_sha1[20]; | 15 | unsigned char new_rev_sha1[20]; |
16 | 16 | ||
17 | static int files, slots; | 17 | static int files, slots; |
18 | static int total_adds, total_rems, max_changes; | 18 | static int total_adds, total_rems, max_changes; |
19 | static int lines_added, lines_removed; | 19 | static int lines_added, lines_removed; |
20 | 20 | ||
21 | static struct fileinfo { | 21 | static struct fileinfo { |
22 | char status; | 22 | char status; |
23 | unsigned char old_sha1[20]; | 23 | unsigned char old_sha1[20]; |
24 | unsigned char new_sha1[20]; | 24 | unsigned char new_sha1[20]; |
25 | unsigned short old_mode; | 25 | unsigned short old_mode; |
26 | unsigned short new_mode; | 26 | unsigned short new_mode; |
27 | char *old_path; | 27 | char *old_path; |
28 | char *new_path; | 28 | char *new_path; |
29 | unsigned int added; | 29 | unsigned int added; |
30 | unsigned int removed; | 30 | unsigned int removed; |
31 | unsigned long old_size; | 31 | unsigned long old_size; |
32 | unsigned long new_size; | 32 | unsigned long new_size; |
33 | int binary:1; | 33 | int binary:1; |
34 | } *items; | 34 | } *items; |
35 | 35 | ||
36 | static int use_ssdiff = 0; | 36 | static int use_ssdiff = 0; |
37 | 37 | ||
38 | static void print_fileinfo(struct fileinfo *info) | 38 | static void print_fileinfo(struct fileinfo *info) |
39 | { | 39 | { |
40 | char *class; | 40 | char *class; |
41 | 41 | ||
42 | switch (info->status) { | 42 | switch (info->status) { |
43 | case DIFF_STATUS_ADDED: | 43 | case DIFF_STATUS_ADDED: |
44 | class = "add"; | 44 | class = "add"; |
45 | break; | 45 | break; |
46 | case DIFF_STATUS_COPIED: | 46 | case DIFF_STATUS_COPIED: |
47 | class = "cpy"; | 47 | class = "cpy"; |
48 | break; | 48 | break; |
49 | case DIFF_STATUS_DELETED: | 49 | case DIFF_STATUS_DELETED: |
50 | class = "del"; | 50 | class = "del"; |
51 | break; | 51 | break; |
52 | case DIFF_STATUS_MODIFIED: | 52 | case DIFF_STATUS_MODIFIED: |
53 | class = "upd"; | 53 | class = "upd"; |
54 | break; | 54 | break; |
55 | case DIFF_STATUS_RENAMED: | 55 | case DIFF_STATUS_RENAMED: |
56 | class = "mov"; | 56 | class = "mov"; |
57 | break; | 57 | break; |
58 | case DIFF_STATUS_TYPE_CHANGED: | 58 | case DIFF_STATUS_TYPE_CHANGED: |
59 | class = "typ"; | 59 | class = "typ"; |
60 | break; | 60 | break; |
61 | case DIFF_STATUS_UNKNOWN: | 61 | case DIFF_STATUS_UNKNOWN: |
62 | class = "unk"; | 62 | class = "unk"; |
63 | break; | 63 | break; |
64 | case DIFF_STATUS_UNMERGED: | 64 | case DIFF_STATUS_UNMERGED: |
65 | class = "stg"; | 65 | class = "stg"; |
66 | break; | 66 | break; |
67 | default: | 67 | default: |
68 | die("bug: unhandled diff status %c", info->status); | 68 | die("bug: unhandled diff status %c", info->status); |
69 | } | 69 | } |
70 | 70 | ||
71 | html("<tr>"); | 71 | html("<tr>"); |
72 | htmlf("<td class='mode'>"); | 72 | htmlf("<td class='mode'>"); |
73 | if (is_null_sha1(info->new_sha1)) { | 73 | if (is_null_sha1(info->new_sha1)) { |
74 | cgit_print_filemode(info->old_mode); | 74 | cgit_print_filemode(info->old_mode); |
75 | } else { | 75 | } else { |
76 | cgit_print_filemode(info->new_mode); | 76 | cgit_print_filemode(info->new_mode); |
77 | } | 77 | } |
78 | 78 | ||
79 | if (info->old_mode != info->new_mode && | 79 | if (info->old_mode != info->new_mode && |
80 | !is_null_sha1(info->old_sha1) && | 80 | !is_null_sha1(info->old_sha1) && |
81 | !is_null_sha1(info->new_sha1)) { | 81 | !is_null_sha1(info->new_sha1)) { |
82 | html("<span class='modechange'>["); | 82 | html("<span class='modechange'>["); |
83 | cgit_print_filemode(info->old_mode); | 83 | cgit_print_filemode(info->old_mode); |
84 | html("]</span>"); | 84 | html("]</span>"); |
85 | } | 85 | } |
86 | htmlf("</td><td class='%s'>", class); | 86 | htmlf("</td><td class='%s'>", class); |
87 | cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, | 87 | cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, |
88 | ctx.qry.sha2, info->new_path, 0); | 88 | ctx.qry.sha2, info->new_path, 0); |
89 | if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) | 89 | if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) |
90 | htmlf(" (%s from %s)", | 90 | htmlf(" (%s from %s)", |
91 | info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", | 91 | info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", |
92 | info->old_path); | 92 | info->old_path); |
93 | html("</td><td class='right'>"); | 93 | html("</td><td class='right'>"); |
94 | if (info->binary) { | 94 | if (info->binary) { |
95 | htmlf("bin</td><td class='graph'>%d -> %d bytes", | 95 | htmlf("bin</td><td class='graph'>%d -> %d bytes", |
96 | info->old_size, info->new_size); | 96 | info->old_size, info->new_size); |
97 | return; | 97 | return; |
98 | } | 98 | } |
99 | htmlf("%d", info->added + info->removed); | 99 | htmlf("%d", info->added + info->removed); |
100 | html("</td><td class='graph'>"); | 100 | html("</td><td class='graph'>"); |
101 | htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); | 101 | htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); |
102 | htmlf("<td class='add' style='width: %.1f%%;'/>", | 102 | htmlf("<td class='add' style='width: %.1f%%;'/>", |
103 | info->added * 100.0 / max_changes); | 103 | info->added * 100.0 / max_changes); |
104 | htmlf("<td class='rem' style='width: %.1f%%;'/>", | 104 | htmlf("<td class='rem' style='width: %.1f%%;'/>", |
105 | info->removed * 100.0 / max_changes); | 105 | info->removed * 100.0 / max_changes); |
106 | htmlf("<td class='none' style='width: %.1f%%;'/>", | 106 | htmlf("<td class='none' style='width: %.1f%%;'/>", |
107 | (max_changes - info->removed - info->added) * 100.0 / max_changes); | 107 | (max_changes - info->removed - info->added) * 100.0 / max_changes); |
108 | html("</tr></table></td></tr>\n"); | 108 | html("</tr></table></td></tr>\n"); |
109 | } | 109 | } |
110 | 110 | ||
111 | static void count_diff_lines(char *line, int len) | 111 | static void count_diff_lines(char *line, int len) |
112 | { | 112 | { |
113 | if (line && (len > 0)) { | 113 | if (line && (len > 0)) { |
114 | if (line[0] == '+') | 114 | if (line[0] == '+') |
115 | lines_added++; | 115 | lines_added++; |
116 | else if (line[0] == '-') | 116 | else if (line[0] == '-') |
117 | lines_removed++; | 117 | lines_removed++; |
118 | } | 118 | } |
119 | } | 119 | } |
120 | 120 | ||
121 | static void inspect_filepair(struct diff_filepair *pair) | 121 | static void inspect_filepair(struct diff_filepair *pair) |
122 | { | 122 | { |
123 | int binary = 0; | 123 | int binary = 0; |
124 | unsigned long old_size = 0; | 124 | unsigned long old_size = 0; |
125 | unsigned long new_size = 0; | 125 | unsigned long new_size = 0; |
126 | files++; | 126 | files++; |
127 | lines_added = 0; | 127 | lines_added = 0; |
128 | lines_removed = 0; | 128 | lines_removed = 0; |
129 | cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size, | 129 | cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size, |
130 | &binary, count_diff_lines); | 130 | &binary, count_diff_lines); |
131 | if (files >= slots) { | 131 | if (files >= slots) { |
132 | if (slots == 0) | 132 | if (slots == 0) |
133 | slots = 4; | 133 | slots = 4; |
134 | else | 134 | else |
135 | slots = slots * 2; | 135 | slots = slots * 2; |
136 | items = xrealloc(items, slots * sizeof(struct fileinfo)); | 136 | items = xrealloc(items, slots * sizeof(struct fileinfo)); |
137 | } | 137 | } |
138 | items[files-1].status = pair->status; | 138 | items[files-1].status = pair->status; |
139 | hashcpy(items[files-1].old_sha1, pair->one->sha1); | 139 | hashcpy(items[files-1].old_sha1, pair->one->sha1); |
140 | hashcpy(items[files-1].new_sha1, pair->two->sha1); | 140 | hashcpy(items[files-1].new_sha1, pair->two->sha1); |
141 | items[files-1].old_mode = pair->one->mode; | 141 | items[files-1].old_mode = pair->one->mode; |
142 | items[files-1].new_mode = pair->two->mode; | 142 | items[files-1].new_mode = pair->two->mode; |
143 | items[files-1].old_path = xstrdup(pair->one->path); | 143 | items[files-1].old_path = xstrdup(pair->one->path); |
144 | items[files-1].new_path = xstrdup(pair->two->path); | 144 | items[files-1].new_path = xstrdup(pair->two->path); |
145 | items[files-1].added = lines_added; | 145 | items[files-1].added = lines_added; |
146 | items[files-1].removed = lines_removed; | 146 | items[files-1].removed = lines_removed; |
147 | items[files-1].old_size = old_size; | 147 | items[files-1].old_size = old_size; |
148 | items[files-1].new_size = new_size; | 148 | items[files-1].new_size = new_size; |
149 | items[files-1].binary = binary; | 149 | items[files-1].binary = binary; |
150 | if (lines_added + lines_removed > max_changes) | 150 | if (lines_added + lines_removed > max_changes) |
151 | max_changes = lines_added + lines_removed; | 151 | max_changes = lines_added + lines_removed; |
152 | total_adds += lines_added; | 152 | total_adds += lines_added; |
153 | total_rems += lines_removed; | 153 | total_rems += lines_removed; |
154 | } | 154 | } |
155 | 155 | ||
156 | void cgit_print_diffstat(const unsigned char *old_sha1, | 156 | void cgit_print_diffstat(const unsigned char *old_sha1, |
157 | const unsigned char *new_sha1) | 157 | const unsigned char *new_sha1) |
158 | { | 158 | { |
159 | int i; | 159 | int i; |
160 | 160 | ||
161 | html("<div class='diffstat-header'>"); | 161 | html("<div class='diffstat-header'>"); |
162 | cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, | 162 | cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, |
163 | ctx.qry.sha2, NULL, 0); | 163 | ctx.qry.sha2, NULL, 0); |
164 | html("</div>"); | 164 | html("</div>"); |
165 | html("<table summary='diffstat' class='diffstat'>"); | 165 | html("<table summary='diffstat' class='diffstat'>"); |
166 | max_changes = 0; | 166 | max_changes = 0; |
167 | cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, NULL); | 167 | cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, NULL); |
168 | for(i = 0; i<files; i++) | 168 | for(i = 0; i<files; i++) |
169 | print_fileinfo(&items[i]); | 169 | print_fileinfo(&items[i]); |
170 | html("</table>"); | 170 | html("</table>"); |
171 | html("<div class='diffstat-summary'>"); | 171 | html("<div class='diffstat-summary'>"); |
172 | htmlf("%d files changed, %d insertions, %d deletions", | 172 | htmlf("%d files changed, %d insertions, %d deletions", |
173 | files, total_adds, total_rems); | 173 | files, total_adds, total_rems); |
174 | html("</div>"); | 174 | html("</div>"); |
175 | } | 175 | } |
176 | 176 | ||
177 | 177 | ||
178 | /* | 178 | /* |
179 | * print a single line returned from xdiff | 179 | * print a single line returned from xdiff |
180 | */ | 180 | */ |
181 | static void print_line(char *line, int len) | 181 | static void print_line(char *line, int len) |
182 | { | 182 | { |
183 | char *class = "ctx"; | 183 | char *class = "ctx"; |
184 | char c = line[len-1]; | 184 | char c = line[len-1]; |
185 | 185 | ||
186 | if (line[0] == '+') | 186 | if (line[0] == '+') |
187 | class = "add"; | 187 | class = "add"; |
188 | else if (line[0] == '-') | 188 | else if (line[0] == '-') |
189 | class = "del"; | 189 | class = "del"; |
190 | else if (line[0] == '@') | 190 | else if (line[0] == '@') |
191 | class = "hunk"; | 191 | class = "hunk"; |
192 | 192 | ||
193 | htmlf("<div class='%s'>", class); | 193 | htmlf("<div class='%s'>", class); |
194 | line[len-1] = '\0'; | 194 | line[len-1] = '\0'; |
195 | html_txt(line); | 195 | html_txt(line); |
196 | html("</div>"); | 196 | html("</div>"); |
197 | line[len-1] = c; | 197 | line[len-1] = c; |
198 | } | 198 | } |
199 | 199 | ||
200 | static void header(unsigned char *sha1, char *path1, int mode1, | 200 | static void header(unsigned char *sha1, char *path1, int mode1, |
201 | unsigned char *sha2, char *path2, int mode2) | 201 | unsigned char *sha2, char *path2, int mode2) |
202 | { | 202 | { |
203 | char *abbrev1, *abbrev2; | 203 | char *abbrev1, *abbrev2; |
204 | int subproject; | 204 | int subproject; |
205 | 205 | ||
206 | subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); | 206 | subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); |
207 | html("<div class='head'>"); | 207 | html("<div class='head'>"); |
208 | html("diff --git a/"); | 208 | html("diff --git a/"); |
209 | html_txt(path1); | 209 | html_txt(path1); |
210 | html(" b/"); | 210 | html(" b/"); |
211 | html_txt(path2); | 211 | html_txt(path2); |
212 | 212 | ||
213 | if (is_null_sha1(sha1)) | 213 | if (is_null_sha1(sha1)) |
214 | path1 = "dev/null"; | 214 | path1 = "dev/null"; |
215 | if (is_null_sha1(sha2)) | 215 | if (is_null_sha1(sha2)) |
216 | path2 = "dev/null"; | 216 | path2 = "dev/null"; |
217 | 217 | ||
218 | if (mode1 == 0) | 218 | if (mode1 == 0) |
219 | htmlf("<br/>new file mode %.6o", mode2); | 219 | htmlf("<br/>new file mode %.6o", mode2); |
220 | 220 | ||
221 | if (mode2 == 0) | 221 | if (mode2 == 0) |
222 | htmlf("<br/>deleted file mode %.6o", mode1); | 222 | htmlf("<br/>deleted file mode %.6o", mode1); |
223 | 223 | ||
224 | if (!subproject) { | 224 | if (!subproject) { |
225 | abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); | 225 | abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); |
226 | abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); | 226 | abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); |
227 | htmlf("<br/>index %s..%s", abbrev1, abbrev2); | 227 | htmlf("<br/>index %s..%s", abbrev1, abbrev2); |
228 | free(abbrev1); | 228 | free(abbrev1); |
229 | free(abbrev2); | 229 | free(abbrev2); |
230 | if (mode1 != 0 && mode2 != 0) { | 230 | if (mode1 != 0 && mode2 != 0) { |
231 | htmlf(" %.6o", mode1); | 231 | htmlf(" %.6o", mode1); |
232 | if (mode2 != mode1) | 232 | if (mode2 != mode1) |
233 | htmlf("..%.6o", mode2); | 233 | htmlf("..%.6o", mode2); |
234 | } | 234 | } |
235 | html("<br/>--- a/"); | 235 | html("<br/>--- a/"); |
236 | if (mode1 != 0) | 236 | if (mode1 != 0) |
237 | cgit_tree_link(path1, NULL, NULL, ctx.qry.head, | 237 | cgit_tree_link(path1, NULL, NULL, ctx.qry.head, |
238 | sha1_to_hex(old_rev_sha1), path1); | 238 | sha1_to_hex(old_rev_sha1), path1); |
239 | else | 239 | else |
240 | html_txt(path1); | 240 | html_txt(path1); |
241 | html("<br/>+++ b/"); | 241 | html("<br/>+++ b/"); |
242 | if (mode2 != 0) | 242 | if (mode2 != 0) |
243 | cgit_tree_link(path2, NULL, NULL, ctx.qry.head, | 243 | cgit_tree_link(path2, NULL, NULL, ctx.qry.head, |
244 | sha1_to_hex(new_rev_sha1), path2); | 244 | sha1_to_hex(new_rev_sha1), path2); |
245 | else | 245 | else |
246 | html_txt(path2); | 246 | html_txt(path2); |
247 | } | 247 | } |
248 | html("</div>"); | 248 | html("</div>"); |
249 | } | 249 | } |
250 | 250 | ||
251 | static void print_ssdiff_link() | 251 | static void print_ssdiff_link() |
252 | { | 252 | { |
253 | if (!strcmp(ctx.qry.page, "diff")) { | 253 | if (!strcmp(ctx.qry.page, "diff")) { |
254 | if (use_ssdiff) | 254 | if (use_ssdiff) |
255 | cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head, | 255 | cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head, |
256 | ctx.qry.sha1, ctx.qry.sha2, NULL, 1); | 256 | ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1); |
257 | else | 257 | else |
258 | cgit_diff_link("Side-by-side diff", NULL, NULL, | 258 | cgit_diff_link("Side-by-side diff", NULL, NULL, |
259 | ctx.qry.head, ctx.qry.sha1, | 259 | ctx.qry.head, ctx.qry.sha1, |
260 | ctx.qry.sha2, NULL, 1); | 260 | ctx.qry.sha2, ctx.qry.path, 1); |
261 | } | 261 | } |
262 | } | 262 | } |
263 | 263 | ||
264 | static void filepair_cb(struct diff_filepair *pair) | 264 | static void filepair_cb(struct diff_filepair *pair) |
265 | { | 265 | { |
266 | unsigned long old_size = 0; | 266 | unsigned long old_size = 0; |
267 | unsigned long new_size = 0; | 267 | unsigned long new_size = 0; |
268 | int binary = 0; | 268 | int binary = 0; |
269 | linediff_fn print_line_fn = print_line; | 269 | linediff_fn print_line_fn = print_line; |
270 | 270 | ||
271 | if (use_ssdiff) { | 271 | if (use_ssdiff) { |
272 | cgit_ssdiff_header_begin(); | 272 | cgit_ssdiff_header_begin(); |
273 | print_line_fn = cgit_ssdiff_line_cb; | 273 | print_line_fn = cgit_ssdiff_line_cb; |
274 | } | 274 | } |
275 | header(pair->one->sha1, pair->one->path, pair->one->mode, | 275 | header(pair->one->sha1, pair->one->path, pair->one->mode, |
276 | pair->two->sha1, pair->two->path, pair->two->mode); | 276 | pair->two->sha1, pair->two->path, pair->two->mode); |
277 | if (use_ssdiff) | 277 | if (use_ssdiff) |
278 | cgit_ssdiff_header_end(); | 278 | cgit_ssdiff_header_end(); |
279 | if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { | 279 | if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { |
280 | if (S_ISGITLINK(pair->one->mode)) | 280 | if (S_ISGITLINK(pair->one->mode)) |
281 | print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); | 281 | print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); |
282 | if (S_ISGITLINK(pair->two->mode)) | 282 | if (S_ISGITLINK(pair->two->mode)) |
283 | print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); | 283 | print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); |
284 | if (use_ssdiff) | ||
285 | cgit_ssdiff_footer(); | ||
284 | return; | 286 | return; |
285 | } | 287 | } |
286 | if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, | 288 | if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, |
287 | &new_size, &binary, print_line_fn)) | 289 | &new_size, &binary, print_line_fn)) |
288 | cgit_print_error("Error running diff"); | 290 | cgit_print_error("Error running diff"); |
289 | if (binary) | 291 | if (binary) { |
290 | print_line_fn(" Binary files differ", 20); | 292 | if (use_ssdiff) |
293 | html("<tr><td colspan='4'>Binary files differ</td></tr>"); | ||
294 | else | ||
295 | html("Binary files differ"); | ||
296 | } | ||
291 | if (use_ssdiff) | 297 | if (use_ssdiff) |
292 | cgit_ssdiff_footer(); | 298 | cgit_ssdiff_footer(); |
293 | } | 299 | } |
294 | 300 | ||
295 | void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) | 301 | void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) |
296 | { | 302 | { |
297 | enum object_type type; | 303 | enum object_type type; |
298 | unsigned long size; | 304 | unsigned long size; |
299 | struct commit *commit, *commit2; | 305 | struct commit *commit, *commit2; |
300 | 306 | ||
301 | if (!new_rev) | 307 | if (!new_rev) |
302 | new_rev = ctx.qry.head; | 308 | new_rev = ctx.qry.head; |
303 | get_sha1(new_rev, new_rev_sha1); | 309 | get_sha1(new_rev, new_rev_sha1); |
304 | type = sha1_object_info(new_rev_sha1, &size); | 310 | type = sha1_object_info(new_rev_sha1, &size); |
305 | if (type == OBJ_BAD) { | 311 | if (type == OBJ_BAD) { |
306 | cgit_print_error(fmt("Bad object name: %s", new_rev)); | 312 | cgit_print_error(fmt("Bad object name: %s", new_rev)); |
307 | return; | 313 | return; |
308 | } | 314 | } |
309 | commit = lookup_commit_reference(new_rev_sha1); | 315 | commit = lookup_commit_reference(new_rev_sha1); |
310 | if (!commit || parse_commit(commit)) | 316 | if (!commit || parse_commit(commit)) |
311 | cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1))); | 317 | cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1))); |
312 | 318 | ||
313 | if (old_rev) | 319 | if (old_rev) |
314 | get_sha1(old_rev, old_rev_sha1); | 320 | get_sha1(old_rev, old_rev_sha1); |
315 | else if (commit->parents && commit->parents->item) | 321 | else if (commit->parents && commit->parents->item) |
316 | hashcpy(old_rev_sha1, commit->parents->item->object.sha1); | 322 | hashcpy(old_rev_sha1, commit->parents->item->object.sha1); |
317 | else | 323 | else |
318 | hashclr(old_rev_sha1); | 324 | hashclr(old_rev_sha1); |
319 | 325 | ||
320 | if (!is_null_sha1(old_rev_sha1)) { | 326 | if (!is_null_sha1(old_rev_sha1)) { |
321 | type = sha1_object_info(old_rev_sha1, &size); | 327 | type = sha1_object_info(old_rev_sha1, &size); |
322 | if (type == OBJ_BAD) { | 328 | if (type == OBJ_BAD) { |
323 | cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1))); | 329 | cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1))); |
324 | return; | 330 | return; |
325 | } | 331 | } |
326 | commit2 = lookup_commit_reference(old_rev_sha1); | 332 | commit2 = lookup_commit_reference(old_rev_sha1); |
327 | if (!commit2 || parse_commit(commit2)) | 333 | if (!commit2 || parse_commit(commit2)) |
328 | cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1))); | 334 | cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1))); |
329 | } | 335 | } |
330 | 336 | ||
331 | if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) | 337 | if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) |
332 | use_ssdiff = 1; | 338 | use_ssdiff = 1; |
333 | 339 | ||
334 | print_ssdiff_link(); | 340 | print_ssdiff_link(); |
335 | cgit_print_diffstat(old_rev_sha1, new_rev_sha1); | 341 | cgit_print_diffstat(old_rev_sha1, new_rev_sha1); |
336 | 342 | ||
337 | if (use_ssdiff) { | 343 | if (use_ssdiff) { |
338 | html("<table summary='ssdiff' class='ssdiff'>"); | 344 | html("<table summary='ssdiff' class='ssdiff'>"); |
339 | } else { | 345 | } else { |
340 | html("<table summary='diff' class='diff'>"); | 346 | html("<table summary='diff' class='diff'>"); |
341 | html("<tr><td>"); | 347 | html("<tr><td>"); |
342 | } | 348 | } |
343 | cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); | 349 | cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); |
344 | if (!use_ssdiff) | 350 | if (!use_ssdiff) |
345 | html("</td></tr>"); | 351 | html("</td></tr>"); |
346 | html("</table>"); | 352 | html("</table>"); |
347 | } | 353 | } |
diff --git a/ui-ssdiff.c b/ui-ssdiff.c index 8215051..5673642 100644 --- a/ui-ssdiff.c +++ b/ui-ssdiff.c | |||
@@ -1,270 +1,274 @@ | |||
1 | #include "cgit.h" | 1 | #include "cgit.h" |
2 | #include "html.h" | 2 | #include "html.h" |
3 | #include "ui-shared.h" | 3 | #include "ui-shared.h" |
4 | 4 | ||
5 | extern int use_ssdiff; | 5 | extern int use_ssdiff; |
6 | 6 | ||
7 | static int current_old_line, current_new_line; | 7 | static int current_old_line, current_new_line; |
8 | 8 | ||
9 | struct deferred_lines { | 9 | struct deferred_lines { |
10 | int line_no; | 10 | int line_no; |
11 | char *line; | 11 | char *line; |
12 | struct deferred_lines *next; | 12 | struct deferred_lines *next; |
13 | }; | 13 | }; |
14 | 14 | ||
15 | static struct deferred_lines *deferred_old, *deferred_old_last; | 15 | static struct deferred_lines *deferred_old, *deferred_old_last; |
16 | static struct deferred_lines *deferred_new, *deferred_new_last; | 16 | static struct deferred_lines *deferred_new, *deferred_new_last; |
17 | 17 | ||
18 | static int line_from_hunk(char *line, char type) | 18 | static int line_from_hunk(char *line, char type) |
19 | { | 19 | { |
20 | char *buf1, *buf2; | 20 | char *buf1, *buf2; |
21 | int len; | 21 | int len; |
22 | 22 | ||
23 | buf1 = strchr(line, type); | 23 | buf1 = strchr(line, type); |
24 | if (buf1 == NULL) | 24 | if (buf1 == NULL) |
25 | return 0; | 25 | return 0; |
26 | buf1 += 1; | 26 | buf1 += 1; |
27 | buf2 = strchr(buf1, ','); | 27 | buf2 = strchr(buf1, ','); |
28 | if (buf2 == NULL) | 28 | if (buf2 == NULL) |
29 | return 0; | 29 | return 0; |
30 | len = buf2 - buf1; | 30 | len = buf2 - buf1; |
31 | buf2 = xmalloc(len + 1); | 31 | buf2 = xmalloc(len + 1); |
32 | strncpy(buf2, buf1, len); | 32 | strncpy(buf2, buf1, len); |
33 | buf2[len] = '\0'; | 33 | buf2[len] = '\0'; |
34 | int res = atoi(buf2); | 34 | int res = atoi(buf2); |
35 | free(buf2); | 35 | free(buf2); |
36 | return res; | 36 | return res; |
37 | } | 37 | } |
38 | 38 | ||
39 | static char *replace_tabs(char *line) | 39 | static char *replace_tabs(char *line) |
40 | { | 40 | { |
41 | char *prev_buf = line; | 41 | char *prev_buf = line; |
42 | char *cur_buf; | 42 | char *cur_buf; |
43 | int linelen = strlen(line); | 43 | int linelen = strlen(line); |
44 | int n_tabs = 0; | 44 | int n_tabs = 0; |
45 | int i; | 45 | int i; |
46 | char *result; | 46 | char *result; |
47 | char *spaces = " "; | 47 | char *spaces = " "; |
48 | 48 | ||
49 | if (linelen == 0) { | 49 | if (linelen == 0) { |
50 | result = xmalloc(1); | 50 | result = xmalloc(1); |
51 | result[0] = '\0'; | 51 | result[0] = '\0'; |
52 | return result; | 52 | return result; |
53 | } | 53 | } |
54 | 54 | ||
55 | for (i = 0; i < linelen; i++) | 55 | for (i = 0; i < linelen; i++) |
56 | if (line[i] == '\t') | 56 | if (line[i] == '\t') |
57 | n_tabs += 1; | 57 | n_tabs += 1; |
58 | result = xmalloc(linelen + n_tabs * 8 + 1); | 58 | result = xmalloc(linelen + n_tabs * 8 + 1); |
59 | result[0] = '\0'; | 59 | result[0] = '\0'; |
60 | 60 | ||
61 | while (1) { | 61 | while (1) { |
62 | cur_buf = strchr(prev_buf, '\t'); | 62 | cur_buf = strchr(prev_buf, '\t'); |
63 | if (!cur_buf) { | 63 | if (!cur_buf) { |
64 | strcat(result, prev_buf); | 64 | strcat(result, prev_buf); |
65 | break; | 65 | break; |
66 | } else { | 66 | } else { |
67 | strcat(result, " "); | 67 | strcat(result, " "); |
68 | strncat(result, spaces, 8 - (strlen(result) % 8)); | 68 | strncat(result, spaces, 8 - (strlen(result) % 8)); |
69 | strncat(result, prev_buf, cur_buf - prev_buf); | 69 | strncat(result, prev_buf, cur_buf - prev_buf); |
70 | } | 70 | } |
71 | prev_buf = cur_buf + 1; | 71 | prev_buf = cur_buf + 1; |
72 | } | 72 | } |
73 | return result; | 73 | return result; |
74 | } | 74 | } |
75 | 75 | ||
76 | static void deferred_old_add(char *line, int line_no) | 76 | static void deferred_old_add(char *line, int line_no) |
77 | { | 77 | { |
78 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); | 78 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); |
79 | item->line = xstrdup(line); | 79 | item->line = xstrdup(line); |
80 | item->line_no = line_no; | 80 | item->line_no = line_no; |
81 | item->next = NULL; | 81 | item->next = NULL; |
82 | if (deferred_old) { | 82 | if (deferred_old) { |
83 | deferred_old_last->next = item; | 83 | deferred_old_last->next = item; |
84 | deferred_old_last = item; | 84 | deferred_old_last = item; |
85 | } else { | 85 | } else { |
86 | deferred_old = deferred_old_last = item; | 86 | deferred_old = deferred_old_last = item; |
87 | } | 87 | } |
88 | } | 88 | } |
89 | 89 | ||
90 | static void deferred_new_add(char *line, int line_no) | 90 | static void deferred_new_add(char *line, int line_no) |
91 | { | 91 | { |
92 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); | 92 | struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); |
93 | item->line = xstrdup(line); | 93 | item->line = xstrdup(line); |
94 | item->line_no = line_no; | 94 | item->line_no = line_no; |
95 | item->next = NULL; | 95 | item->next = NULL; |
96 | if (deferred_new) { | 96 | if (deferred_new) { |
97 | deferred_new_last->next = item; | 97 | deferred_new_last->next = item; |
98 | deferred_new_last = item; | 98 | deferred_new_last = item; |
99 | } else { | 99 | } else { |
100 | deferred_new = deferred_new_last = item; | 100 | deferred_new = deferred_new_last = item; |
101 | } | 101 | } |
102 | } | 102 | } |
103 | 103 | ||
104 | static void print_ssdiff_line(char *class, int old_line_no, char *old_line, | 104 | static void print_ssdiff_line(char *class, int old_line_no, char *old_line, |
105 | int new_line_no, char *new_line) | 105 | int new_line_no, char *new_line) |
106 | { | 106 | { |
107 | html("<tr>"); | 107 | html("<tr>"); |
108 | if (old_line_no > 0) | 108 | if (old_line_no > 0) |
109 | htmlf("<td class='lineno'>%d</td><td class='%s'>", | 109 | htmlf("<td class='lineno'>%d</td><td class='%s'>", |
110 | old_line_no, class); | 110 | old_line_no, class); |
111 | else if (old_line) | ||
112 | htmlf("<td class='lineno'></td><td class='%s'>", class); | ||
111 | else | 113 | else |
112 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); | 114 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); |
113 | 115 | ||
114 | if (old_line) { | 116 | if (old_line) { |
115 | old_line = replace_tabs(old_line + 1); | 117 | old_line = replace_tabs(old_line + 1); |
116 | html_txt(old_line); | 118 | html_txt(old_line); |
117 | free(old_line); | 119 | free(old_line); |
118 | } | 120 | } |
119 | 121 | ||
120 | html("</td>"); | 122 | html("</td>"); |
121 | 123 | ||
122 | if (new_line_no > 0) | 124 | if (new_line_no > 0) |
123 | htmlf("<td class='lineno'>%d</td><td class='%s'>", | 125 | htmlf("<td class='lineno'>%d</td><td class='%s'>", |
124 | new_line_no, class); | 126 | new_line_no, class); |
127 | else if (new_line) | ||
128 | htmlf("<td class='lineno'></td><td class='%s'>", class); | ||
125 | else | 129 | else |
126 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); | 130 | htmlf("<td class='lineno'></td><td class='%s_dark'>", class); |
127 | 131 | ||
128 | if (new_line) { | 132 | if (new_line) { |
129 | new_line = replace_tabs(new_line + 1); | 133 | new_line = replace_tabs(new_line + 1); |
130 | html_txt(new_line); | 134 | html_txt(new_line); |
131 | free(new_line); | 135 | free(new_line); |
132 | } | 136 | } |
133 | 137 | ||
134 | html("</td></tr>"); | 138 | html("</td></tr>"); |
135 | } | 139 | } |
136 | 140 | ||
137 | static void print_deferred_old_lines() | 141 | static void print_deferred_old_lines() |
138 | { | 142 | { |
139 | struct deferred_lines *iter_old, *tmp; | 143 | struct deferred_lines *iter_old, *tmp; |
140 | 144 | ||
141 | iter_old = deferred_old; | 145 | iter_old = deferred_old; |
142 | while (iter_old) { | 146 | while (iter_old) { |
143 | print_ssdiff_line("del", iter_old->line_no, | 147 | print_ssdiff_line("del", iter_old->line_no, |
144 | iter_old->line, -1, NULL); | 148 | iter_old->line, -1, NULL); |
145 | tmp = iter_old->next; | 149 | tmp = iter_old->next; |
146 | free(iter_old); | 150 | free(iter_old); |
147 | iter_old = tmp; | 151 | iter_old = tmp; |
148 | } | 152 | } |
149 | } | 153 | } |
150 | 154 | ||
151 | static void print_deferred_new_lines() | 155 | static void print_deferred_new_lines() |
152 | { | 156 | { |
153 | struct deferred_lines *iter_new, *tmp; | 157 | struct deferred_lines *iter_new, *tmp; |
154 | 158 | ||
155 | iter_new = deferred_new; | 159 | iter_new = deferred_new; |
156 | while (iter_new) { | 160 | while (iter_new) { |
157 | print_ssdiff_line("add", -1, NULL, iter_new->line_no, | 161 | print_ssdiff_line("add", -1, NULL, iter_new->line_no, |
158 | iter_new->line); | 162 | iter_new->line); |
159 | tmp = iter_new->next; | 163 | tmp = iter_new->next; |
160 | free(iter_new); | 164 | free(iter_new); |
161 | iter_new = tmp; | 165 | iter_new = tmp; |
162 | } | 166 | } |
163 | } | 167 | } |
164 | 168 | ||
165 | static void print_deferred_changed_lines() | 169 | static void print_deferred_changed_lines() |
166 | { | 170 | { |
167 | struct deferred_lines *iter_old, *iter_new, *tmp; | 171 | struct deferred_lines *iter_old, *iter_new, *tmp; |
168 | 172 | ||
169 | iter_old = deferred_old; | 173 | iter_old = deferred_old; |
170 | iter_new = deferred_new; | 174 | iter_new = deferred_new; |
171 | while (iter_old || iter_new) { | 175 | while (iter_old || iter_new) { |
172 | if (iter_old && iter_new) | 176 | if (iter_old && iter_new) |
173 | print_ssdiff_line("changed", iter_old->line_no, | 177 | print_ssdiff_line("changed", iter_old->line_no, |
174 | iter_old->line, | 178 | iter_old->line, |
175 | iter_new->line_no, iter_new->line); | 179 | iter_new->line_no, iter_new->line); |
176 | else if (iter_old) | 180 | else if (iter_old) |
177 | print_ssdiff_line("changed", iter_old->line_no, | 181 | print_ssdiff_line("changed", iter_old->line_no, |
178 | iter_old->line, -1, NULL); | 182 | iter_old->line, -1, NULL); |
179 | else if (iter_new) | 183 | else if (iter_new) |
180 | print_ssdiff_line("changed", -1, NULL, | 184 | print_ssdiff_line("changed", -1, NULL, |
181 | iter_new->line_no, iter_new->line); | 185 | iter_new->line_no, iter_new->line); |
182 | 186 | ||
183 | if (iter_old) { | 187 | if (iter_old) { |
184 | tmp = iter_old->next; | 188 | tmp = iter_old->next; |
185 | free(iter_old); | 189 | free(iter_old); |
186 | iter_old = tmp; | 190 | iter_old = tmp; |
187 | } | 191 | } |
188 | 192 | ||
189 | if (iter_new) { | 193 | if (iter_new) { |
190 | tmp = iter_new->next; | 194 | tmp = iter_new->next; |
191 | free(iter_new); | 195 | free(iter_new); |
192 | iter_new = tmp; | 196 | iter_new = tmp; |
193 | } | 197 | } |
194 | } | 198 | } |
195 | } | 199 | } |
196 | 200 | ||
197 | void cgit_ssdiff_print_deferred_lines() | 201 | void cgit_ssdiff_print_deferred_lines() |
198 | { | 202 | { |
199 | if (!deferred_old && !deferred_new) | 203 | if (!deferred_old && !deferred_new) |
200 | return; | 204 | return; |
201 | 205 | ||
202 | if (deferred_old && !deferred_new) | 206 | if (deferred_old && !deferred_new) |
203 | print_deferred_old_lines(); | 207 | print_deferred_old_lines(); |
204 | else if (!deferred_old && deferred_new) | 208 | else if (!deferred_old && deferred_new) |
205 | print_deferred_new_lines(); | 209 | print_deferred_new_lines(); |
206 | else | 210 | else |
207 | print_deferred_changed_lines(); | 211 | print_deferred_changed_lines(); |
208 | 212 | ||
209 | deferred_old = deferred_old_last = NULL; | 213 | deferred_old = deferred_old_last = NULL; |
210 | deferred_new = deferred_new_last = NULL; | 214 | deferred_new = deferred_new_last = NULL; |
211 | } | 215 | } |
212 | 216 | ||
213 | /* | 217 | /* |
214 | * print a single line returned from xdiff | 218 | * print a single line returned from xdiff |
215 | */ | 219 | */ |
216 | void cgit_ssdiff_line_cb(char *line, int len) | 220 | void cgit_ssdiff_line_cb(char *line, int len) |
217 | { | 221 | { |
218 | char c = line[len - 1]; | 222 | char c = line[len - 1]; |
219 | 223 | ||
220 | line[len - 1] = '\0'; | 224 | line[len - 1] = '\0'; |
221 | 225 | ||
222 | if (line[0] == '@') { | 226 | if (line[0] == '@') { |
223 | current_old_line = line_from_hunk(line, '-'); | 227 | current_old_line = line_from_hunk(line, '-'); |
224 | current_new_line = line_from_hunk(line, '+'); | 228 | current_new_line = line_from_hunk(line, '+'); |
225 | } | 229 | } |
226 | 230 | ||
227 | if (line[0] == ' ') { | 231 | if (line[0] == ' ') { |
228 | if (deferred_old || deferred_new) | 232 | if (deferred_old || deferred_new) |
229 | cgit_ssdiff_print_deferred_lines(); | 233 | cgit_ssdiff_print_deferred_lines(); |
230 | print_ssdiff_line("ctx", current_old_line, line, | 234 | print_ssdiff_line("ctx", current_old_line, line, |
231 | current_new_line, line); | 235 | current_new_line, line); |
232 | current_old_line += 1; | 236 | current_old_line += 1; |
233 | current_new_line += 1; | 237 | current_new_line += 1; |
234 | } else if (line[0] == '+') { | 238 | } else if (line[0] == '+') { |
235 | deferred_new_add(line, current_new_line); | 239 | deferred_new_add(line, current_new_line); |
236 | current_new_line += 1; | 240 | current_new_line += 1; |
237 | } else if (line[0] == '-') { | 241 | } else if (line[0] == '-') { |
238 | deferred_old_add(line, current_old_line); | 242 | deferred_old_add(line, current_old_line); |
239 | current_old_line += 1; | 243 | current_old_line += 1; |
240 | } else if (line[0] == '@') { | 244 | } else if (line[0] == '@') { |
241 | html("<tr><td colspan='4' class='hunk'>"); | 245 | html("<tr><td colspan='4' class='hunk'>"); |
242 | html_txt(line); | 246 | html_txt(line); |
243 | html("</td></tr>"); | 247 | html("</td></tr>"); |
244 | } else { | 248 | } else { |
245 | html("<tr><td colspan='4' class='ctx'>"); | 249 | html("<tr><td colspan='4' class='ctx'>"); |
246 | html_txt(line); | 250 | html_txt(line); |
247 | html("</td></tr>"); | 251 | html("</td></tr>"); |
248 | } | 252 | } |
249 | line[len - 1] = c; | 253 | line[len - 1] = c; |
250 | } | 254 | } |
251 | 255 | ||
252 | void cgit_ssdiff_header_begin() | 256 | void cgit_ssdiff_header_begin() |
253 | { | 257 | { |
254 | current_old_line = 0; | 258 | current_old_line = -1; |
255 | current_new_line = 0; | 259 | current_new_line = -1; |
256 | html("<tr><td class='space' colspan='4'><div></div></td></tr>"); | 260 | html("<tr><td class='space' colspan='4'><div></div></td></tr>"); |
257 | html("<tr><td class='head' colspan='4'>"); | 261 | html("<tr><td class='head' colspan='4'>"); |
258 | } | 262 | } |
259 | 263 | ||
260 | void cgit_ssdiff_header_end() | 264 | void cgit_ssdiff_header_end() |
261 | { | 265 | { |
262 | html("</td><tr>"); | 266 | html("</td><tr>"); |
263 | } | 267 | } |
264 | 268 | ||
265 | void cgit_ssdiff_footer() | 269 | void cgit_ssdiff_footer() |
266 | { | 270 | { |
267 | if (deferred_old || deferred_new) | 271 | if (deferred_old || deferred_new) |
268 | cgit_ssdiff_print_deferred_lines(); | 272 | cgit_ssdiff_print_deferred_lines(); |
269 | html("<tr><td class='foot' colspan='4'></td></tr>"); | 273 | html("<tr><td class='foot' colspan='4'></td></tr>"); |
270 | } | 274 | } |