summaryrefslogtreecommitdiffabout
authorRagnar Ouchterlony <ragnar@lysator.liu.se>2009-10-25 17:13:22 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2009-11-07 14:37:11 (UTC)
commit735e15e38a484bf0daa98776fa7cde270a271cda (patch) (unidiff)
tree588545cead471e8997adc1ae5abca164a4873481
parent4a198e4b8ee62a9a8b5156a46bfce46dc7223fe9 (diff)
downloadcgit-735e15e38a484bf0daa98776fa7cde270a271cda.zip
cgit-735e15e38a484bf0daa98776fa7cde270a271cda.tar.gz
cgit-735e15e38a484bf0daa98776fa7cde270a271cda.tar.bz2
In side-by-side diff, add support for marking individual characters.
Refuses to do so if the left hand side of the diff has different amount of differing lines to the right hand side to avoid confusion. Note that I use the naive dynamic programming approach for calculating the longest common subsequence. We could probably be more efficient by using a better algorithm. The LCS calculating function is O(n*m) and uses up n*m amount of memory too (so if we we compare two strings of length 100, I use an array of 10000 for calculating the LCS). Might want to not calculate LCS if the length of the line is too large. Signed-off-by: Ragnar Ouchterlony <ragnar@lysator.liu.se>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.css10
-rw-r--r--ui-ssdiff.c145
2 files changed, 130 insertions, 25 deletions
diff --git a/cgit.css b/cgit.css
index 3f37165..9e6d2a4 100644
--- a/cgit.css
+++ b/cgit.css
@@ -566,127 +566,137 @@ table.vgraph {
566 566
567table.vgraph th { 567table.vgraph th {
568 background-color: #eee; 568 background-color: #eee;
569 font-weight: bold; 569 font-weight: bold;
570 border: solid 1px white; 570 border: solid 1px white;
571 padding: 1px 0.5em; 571 padding: 1px 0.5em;
572} 572}
573 573
574table.vgraph td { 574table.vgraph td {
575 vertical-align: bottom; 575 vertical-align: bottom;
576 padding: 0px 10px; 576 padding: 0px 10px;
577} 577}
578 578
579table.vgraph div.bar { 579table.vgraph div.bar {
580 background-color: #eee; 580 background-color: #eee;
581} 581}
582 582
583table.hgraph { 583table.hgraph {
584 border: solid 1px black; 584 border: solid 1px black;
585 width: 800px; 585 width: 800px;
586} 586}
587 587
588table.hgraph th { 588table.hgraph th {
589 background-color: #eee; 589 background-color: #eee;
590 font-weight: bold; 590 font-weight: bold;
591 border: solid 1px black; 591 border: solid 1px black;
592 padding: 1px 0.5em; 592 padding: 1px 0.5em;
593} 593}
594 594
595table.hgraph td { 595table.hgraph td {
596 vertical-align: center; 596 vertical-align: center;
597 padding: 2px 2px; 597 padding: 2px 2px;
598} 598}
599 599
600table.hgraph div.bar { 600table.hgraph div.bar {
601 background-color: #eee; 601 background-color: #eee;
602 height: 1em; 602 height: 1em;
603} 603}
604 604
605table.ssdiff { 605table.ssdiff {
606 width: 100%; 606 width: 100%;
607} 607}
608 608
609table.ssdiff td { 609table.ssdiff td {
610 font-size: 75%; 610 font-size: 75%;
611 font-family: monospace; 611 font-family: monospace;
612 white-space: pre; 612 white-space: pre;
613 padding: 1px 4px 1px 4px; 613 padding: 1px 4px 1px 4px;
614 border-left: solid 1px #aaa; 614 border-left: solid 1px #aaa;
615 border-right: solid 1px #aaa; 615 border-right: solid 1px #aaa;
616} 616}
617 617
618table.ssdiff td.add { 618table.ssdiff td.add {
619 color: black; 619 color: black;
620 background: #cfc; 620 background: #cfc;
621 min-width: 50%; 621 min-width: 50%;
622} 622}
623 623
624table.ssdiff td.add_dark { 624table.ssdiff td.add_dark {
625 color: black; 625 color: black;
626 background: #aca; 626 background: #aca;
627 min-width: 50%; 627 min-width: 50%;
628} 628}
629 629
630table.ssdiff span.add {
631 background: #cfc;
632 font-weight: bold;
633}
634
630table.ssdiff td.del { 635table.ssdiff td.del {
631 color: black; 636 color: black;
632 background: #fcc; 637 background: #fcc;
633 min-width: 50%; 638 min-width: 50%;
634} 639}
635 640
636table.ssdiff td.del_dark { 641table.ssdiff td.del_dark {
637 color: black; 642 color: black;
638 background: #caa; 643 background: #caa;
639 min-width: 50%; 644 min-width: 50%;
640} 645}
641 646
647table.ssdiff span.del {
648 background: #fcc;
649 font-weight: bold;
650}
651
642table.ssdiff td.changed { 652table.ssdiff td.changed {
643 color: black; 653 color: black;
644 background: #ffc; 654 background: #ffc;
645 min-width: 50%; 655 min-width: 50%;
646} 656}
647 657
648table.ssdiff td.changed_dark { 658table.ssdiff td.changed_dark {
649 color: black; 659 color: black;
650 background: #cca; 660 background: #cca;
651 min-width: 50%; 661 min-width: 50%;
652} 662}
653 663
654table.ssdiff td.lineno { 664table.ssdiff td.lineno {
655 color: black; 665 color: black;
656 background: #eee; 666 background: #eee;
657 text-align: right; 667 text-align: right;
658 width: 3em; 668 width: 3em;
659 min-width: 3em; 669 min-width: 3em;
660} 670}
661 671
662table.ssdiff td.hunk { 672table.ssdiff td.hunk {
663 color: #black; 673 color: #black;
664 background: #ccf; 674 background: #ccf;
665 border-top: solid 1px #aaa; 675 border-top: solid 1px #aaa;
666 border-bottom: solid 1px #aaa; 676 border-bottom: solid 1px #aaa;
667} 677}
668 678
669table.ssdiff td.head { 679table.ssdiff td.head {
670 border-top: solid 1px #aaa; 680 border-top: solid 1px #aaa;
671 border-bottom: solid 1px #aaa; 681 border-bottom: solid 1px #aaa;
672} 682}
673 683
674table.ssdiff td.head div.head { 684table.ssdiff td.head div.head {
675 font-weight: bold; 685 font-weight: bold;
676 color: black; 686 color: black;
677} 687}
678 688
679table.ssdiff td.foot { 689table.ssdiff td.foot {
680 border-top: solid 1px #aaa; 690 border-top: solid 1px #aaa;
681 border-left: none; 691 border-left: none;
682 border-right: none; 692 border-right: none;
683 border-bottom: none; 693 border-bottom: none;
684} 694}
685 695
686table.ssdiff td.space { 696table.ssdiff td.space {
687 border: none; 697 border: none;
688} 698}
689 699
690table.ssdiff td.space div { 700table.ssdiff td.space div {
691 min-height: 3em; 701 min-height: 3em;
692} \ No newline at end of file 702} \ No newline at end of file
diff --git a/ui-ssdiff.c b/ui-ssdiff.c
index 5673642..408e620 100644
--- a/ui-ssdiff.c
+++ b/ui-ssdiff.c
@@ -1,274 +1,369 @@
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
5extern int use_ssdiff; 5extern int use_ssdiff;
6 6
7static int current_old_line, current_new_line; 7static int current_old_line, current_new_line;
8 8
9struct deferred_lines { 9struct 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
15static struct deferred_lines *deferred_old, *deferred_old_last; 15static struct deferred_lines *deferred_old, *deferred_old_last;
16static struct deferred_lines *deferred_new, *deferred_new_last; 16static struct deferred_lines *deferred_new, *deferred_new_last;
17 17
18static char *longest_common_subsequence(char *A, char *B)
19{
20 int i, j, ri;
21 int m = strlen(A);
22 int n = strlen(B);
23 int L[m + 1][n + 1];
24 int tmp1, tmp2;
25 int lcs_length;
26 char *result;
27
28 for (i = m; i >= 0; i--) {
29 for (j = n; j >= 0; j--) {
30 if (A[i] == '\0' || B[j] == '\0') {
31 L[i][j] = 0;
32 } else if (A[i] == B[j]) {
33 L[i][j] = 1 + L[i + 1][j + 1];
34 } else {
35 tmp1 = L[i + 1][j];
36 tmp2 = L[i][j + 1];
37 L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
38 }
39 }
40 }
41
42 lcs_length = L[0][0];
43 result = xmalloc(lcs_length + 2);
44 memset(result, 0, sizeof(*result) * (lcs_length + 2));
45
46 ri = 0;
47 i = 0;
48 j = 0;
49 while (i < m && j < n) {
50 if (A[i] == B[j]) {
51 result[ri] = A[i];
52 ri += 1;
53 i += 1;
54 j += 1;
55 } else if (L[i + 1][j] >= L[i][j + 1]) {
56 i += 1;
57 } else {
58 j += 1;
59 }
60 }
61 return result;
62}
63
18static int line_from_hunk(char *line, char type) 64static int line_from_hunk(char *line, char type)
19{ 65{
20 char *buf1, *buf2; 66 char *buf1, *buf2;
21 int len; 67 int len;
22 68
23 buf1 = strchr(line, type); 69 buf1 = strchr(line, type);
24 if (buf1 == NULL) 70 if (buf1 == NULL)
25 return 0; 71 return 0;
26 buf1 += 1; 72 buf1 += 1;
27 buf2 = strchr(buf1, ','); 73 buf2 = strchr(buf1, ',');
28 if (buf2 == NULL) 74 if (buf2 == NULL)
29 return 0; 75 return 0;
30 len = buf2 - buf1; 76 len = buf2 - buf1;
31 buf2 = xmalloc(len + 1); 77 buf2 = xmalloc(len + 1);
32 strncpy(buf2, buf1, len); 78 strncpy(buf2, buf1, len);
33 buf2[len] = '\0'; 79 buf2[len] = '\0';
34 int res = atoi(buf2); 80 int res = atoi(buf2);
35 free(buf2); 81 free(buf2);
36 return res; 82 return res;
37} 83}
38 84
39static char *replace_tabs(char *line) 85static char *replace_tabs(char *line)
40{ 86{
41 char *prev_buf = line; 87 char *prev_buf = line;
42 char *cur_buf; 88 char *cur_buf;
43 int linelen = strlen(line); 89 int linelen = strlen(line);
44 int n_tabs = 0; 90 int n_tabs = 0;
45 int i; 91 int i;
46 char *result; 92 char *result;
47 char *spaces = " "; 93 char *spaces = " ";
48 94
49 if (linelen == 0) { 95 if (linelen == 0) {
50 result = xmalloc(1); 96 result = xmalloc(1);
51 result[0] = '\0'; 97 result[0] = '\0';
52 return result; 98 return result;
53 } 99 }
54 100
55 for (i = 0; i < linelen; i++) 101 for (i = 0; i < linelen; i++)
56 if (line[i] == '\t') 102 if (line[i] == '\t')
57 n_tabs += 1; 103 n_tabs += 1;
58 result = xmalloc(linelen + n_tabs * 8 + 1); 104 result = xmalloc(linelen + n_tabs * 8 + 1);
59 result[0] = '\0'; 105 result[0] = '\0';
60 106
61 while (1) { 107 while (1) {
62 cur_buf = strchr(prev_buf, '\t'); 108 cur_buf = strchr(prev_buf, '\t');
63 if (!cur_buf) { 109 if (!cur_buf) {
64 strcat(result, prev_buf); 110 strcat(result, prev_buf);
65 break; 111 break;
66 } else { 112 } else {
67 strcat(result, " "); 113 strcat(result, " ");
68 strncat(result, spaces, 8 - (strlen(result) % 8)); 114 strncat(result, spaces, 8 - (strlen(result) % 8));
69 strncat(result, prev_buf, cur_buf - prev_buf); 115 strncat(result, prev_buf, cur_buf - prev_buf);
70 } 116 }
71 prev_buf = cur_buf + 1; 117 prev_buf = cur_buf + 1;
72 } 118 }
73 return result; 119 return result;
74} 120}
75 121
122static int calc_deferred_lines(struct deferred_lines *start)
123{
124 struct deferred_lines *item = start;
125 int result = 0;
126 while (item) {
127 result += 1;
128 item = item->next;
129 }
130 return result;
131}
132
76static void deferred_old_add(char *line, int line_no) 133static void deferred_old_add(char *line, int line_no)
77{ 134{
78 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); 135 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
79 item->line = xstrdup(line); 136 item->line = xstrdup(line);
80 item->line_no = line_no; 137 item->line_no = line_no;
81 item->next = NULL; 138 item->next = NULL;
82 if (deferred_old) { 139 if (deferred_old) {
83 deferred_old_last->next = item; 140 deferred_old_last->next = item;
84 deferred_old_last = item; 141 deferred_old_last = item;
85 } else { 142 } else {
86 deferred_old = deferred_old_last = item; 143 deferred_old = deferred_old_last = item;
87 } 144 }
88} 145}
89 146
90static void deferred_new_add(char *line, int line_no) 147static void deferred_new_add(char *line, int line_no)
91{ 148{
92 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); 149 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
93 item->line = xstrdup(line); 150 item->line = xstrdup(line);
94 item->line_no = line_no; 151 item->line_no = line_no;
95 item->next = NULL; 152 item->next = NULL;
96 if (deferred_new) { 153 if (deferred_new) {
97 deferred_new_last->next = item; 154 deferred_new_last->next = item;
98 deferred_new_last = item; 155 deferred_new_last = item;
99 } else { 156 } else {
100 deferred_new = deferred_new_last = item; 157 deferred_new = deferred_new_last = item;
101 } 158 }
102} 159}
103 160
104static void print_ssdiff_line(char *class, int old_line_no, char *old_line, 161static void print_part_with_lcs(char *class, char *line, char *lcs)
105 int new_line_no, char *new_line) 162{
163 int line_len = strlen(line);
164 int i, j;
165 char c[2] = " ";
166 int same = 1;
167
168 j = 0;
169 for (i = 0; i < line_len; i++) {
170 c[0] = line[i];
171 if (same) {
172 if (line[i] == lcs[j])
173 j += 1;
174 else {
175 same = 0;
176 htmlf("<span class='%s'>", class);
177 }
178 } else if (line[i] == lcs[j]) {
179 same = 1;
180 htmlf("</span>");
181 j += 1;
182 }
183 html_txt(c);
184 }
185}
186
187static void print_ssdiff_line(char *class,
188 int old_line_no,
189 char *old_line,
190 int new_line_no,
191 char *new_line, int individual_chars)
106{ 192{
193 char *lcs = NULL;
194 if (old_line)
195 old_line = replace_tabs(old_line + 1);
196 if (new_line)
197 new_line = replace_tabs(new_line + 1);
198 if (individual_chars && old_line && new_line)
199 lcs = longest_common_subsequence(old_line, new_line);
107 html("<tr>"); 200 html("<tr>");
108 if (old_line_no > 0) 201 if (old_line_no > 0)
109 htmlf("<td class='lineno'>%d</td><td class='%s'>", 202 htmlf("<td class='lineno'>%d</td><td class='%s'>",
110 old_line_no, class); 203 old_line_no, class);
111 else if (old_line) 204 else if (old_line)
112 htmlf("<td class='lineno'></td><td class='%s'>", class); 205 htmlf("<td class='lineno'></td><td class='%s'>", class);
113 else 206 else
114 htmlf("<td class='lineno'></td><td class='%s_dark'>", class); 207 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
115
116 if (old_line) { 208 if (old_line) {
117 old_line = replace_tabs(old_line + 1); 209 if (lcs)
118 html_txt(old_line); 210 print_part_with_lcs("del", old_line, lcs);
119 free(old_line); 211 else
212 html_txt(old_line);
120 } 213 }
121 214
122 html("</td>"); 215 html("</td>");
123
124 if (new_line_no > 0) 216 if (new_line_no > 0)
125 htmlf("<td class='lineno'>%d</td><td class='%s'>", 217 htmlf("<td class='lineno'>%d</td><td class='%s'>",
126 new_line_no, class); 218 new_line_no, class);
127 else if (new_line) 219 else if (new_line)
128 htmlf("<td class='lineno'></td><td class='%s'>", class); 220 htmlf("<td class='lineno'></td><td class='%s'>", class);
129 else 221 else
130 htmlf("<td class='lineno'></td><td class='%s_dark'>", class); 222 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
131
132 if (new_line) { 223 if (new_line) {
133 new_line = replace_tabs(new_line + 1); 224 if (lcs)
134 html_txt(new_line); 225 print_part_with_lcs("add", new_line, lcs);
135 free(new_line); 226 else
227 html_txt(new_line);
136 } 228 }
137 229
138 html("</td></tr>"); 230 html("</td></tr>");
231 if (lcs)
232 free(lcs);
233 if (new_line)
234 free(new_line);
235 if (old_line)
236 free(old_line);
139} 237}
140 238
141static void print_deferred_old_lines() 239static void print_deferred_old_lines()
142{ 240{
143 struct deferred_lines *iter_old, *tmp; 241 struct deferred_lines *iter_old, *tmp;
144
145 iter_old = deferred_old; 242 iter_old = deferred_old;
146 while (iter_old) { 243 while (iter_old) {
147 print_ssdiff_line("del", iter_old->line_no, 244 print_ssdiff_line("del", iter_old->line_no,
148 iter_old->line, -1, NULL); 245 iter_old->line, -1, NULL, 0);
149 tmp = iter_old->next; 246 tmp = iter_old->next;
150 free(iter_old); 247 free(iter_old);
151 iter_old = tmp; 248 iter_old = tmp;
152 } 249 }
153} 250}
154 251
155static void print_deferred_new_lines() 252static void print_deferred_new_lines()
156{ 253{
157 struct deferred_lines *iter_new, *tmp; 254 struct deferred_lines *iter_new, *tmp;
158
159 iter_new = deferred_new; 255 iter_new = deferred_new;
160 while (iter_new) { 256 while (iter_new) {
161 print_ssdiff_line("add", -1, NULL, iter_new->line_no, 257 print_ssdiff_line("add", -1, NULL,
162 iter_new->line); 258 iter_new->line_no, iter_new->line, 0);
163 tmp = iter_new->next; 259 tmp = iter_new->next;
164 free(iter_new); 260 free(iter_new);
165 iter_new = tmp; 261 iter_new = tmp;
166 } 262 }
167} 263}
168 264
169static void print_deferred_changed_lines() 265static void print_deferred_changed_lines()
170{ 266{
171 struct deferred_lines *iter_old, *iter_new, *tmp; 267 struct deferred_lines *iter_old, *iter_new, *tmp;
268 int n_old_lines = calc_deferred_lines(deferred_old);
269 int n_new_lines = calc_deferred_lines(deferred_new);
270 int individual_chars = (n_old_lines == n_new_lines ? 1 : 0);
172 271
173 iter_old = deferred_old; 272 iter_old = deferred_old;
174 iter_new = deferred_new; 273 iter_new = deferred_new;
175 while (iter_old || iter_new) { 274 while (iter_old || iter_new) {
176 if (iter_old && iter_new) 275 if (iter_old && iter_new)
177 print_ssdiff_line("changed", iter_old->line_no, 276 print_ssdiff_line("changed", iter_old->line_no,
178 iter_old->line, 277 iter_old->line,
179 iter_new->line_no, iter_new->line); 278 iter_new->line_no, iter_new->line,
279 individual_chars);
180 else if (iter_old) 280 else if (iter_old)
181 print_ssdiff_line("changed", iter_old->line_no, 281 print_ssdiff_line("changed", iter_old->line_no,
182 iter_old->line, -1, NULL); 282 iter_old->line, -1, NULL, 0);
183 else if (iter_new) 283 else if (iter_new)
184 print_ssdiff_line("changed", -1, NULL, 284 print_ssdiff_line("changed", -1, NULL,
185 iter_new->line_no, iter_new->line); 285 iter_new->line_no, iter_new->line, 0);
186
187 if (iter_old) { 286 if (iter_old) {
188 tmp = iter_old->next; 287 tmp = iter_old->next;
189 free(iter_old); 288 free(iter_old);
190 iter_old = tmp; 289 iter_old = tmp;
191 } 290 }
192 291
193 if (iter_new) { 292 if (iter_new) {
194 tmp = iter_new->next; 293 tmp = iter_new->next;
195 free(iter_new); 294 free(iter_new);
196 iter_new = tmp; 295 iter_new = tmp;
197 } 296 }
198 } 297 }
199} 298}
200 299
201void cgit_ssdiff_print_deferred_lines() 300void cgit_ssdiff_print_deferred_lines()
202{ 301{
203 if (!deferred_old && !deferred_new) 302 if (!deferred_old && !deferred_new)
204 return; 303 return;
205
206 if (deferred_old && !deferred_new) 304 if (deferred_old && !deferred_new)
207 print_deferred_old_lines(); 305 print_deferred_old_lines();
208 else if (!deferred_old && deferred_new) 306 else if (!deferred_old && deferred_new)
209 print_deferred_new_lines(); 307 print_deferred_new_lines();
210 else 308 else
211 print_deferred_changed_lines(); 309 print_deferred_changed_lines();
212
213 deferred_old = deferred_old_last = NULL; 310 deferred_old = deferred_old_last = NULL;
214 deferred_new = deferred_new_last = NULL; 311 deferred_new = deferred_new_last = NULL;
215} 312}
216 313
217/* 314/*
218 * print a single line returned from xdiff 315 * print a single line returned from xdiff
219 */ 316 */
220void cgit_ssdiff_line_cb(char *line, int len) 317void cgit_ssdiff_line_cb(char *line, int len)
221{ 318{
222 char c = line[len - 1]; 319 char c = line[len - 1];
223
224 line[len - 1] = '\0'; 320 line[len - 1] = '\0';
225
226 if (line[0] == '@') { 321 if (line[0] == '@') {
227 current_old_line = line_from_hunk(line, '-'); 322 current_old_line = line_from_hunk(line, '-');
228 current_new_line = line_from_hunk(line, '+'); 323 current_new_line = line_from_hunk(line, '+');
229 } 324 }
230 325
231 if (line[0] == ' ') { 326 if (line[0] == ' ') {
232 if (deferred_old || deferred_new) 327 if (deferred_old || deferred_new)
233 cgit_ssdiff_print_deferred_lines(); 328 cgit_ssdiff_print_deferred_lines();
234 print_ssdiff_line("ctx", current_old_line, line, 329 print_ssdiff_line("ctx", current_old_line, line,
235 current_new_line, line); 330 current_new_line, line, 0);
236 current_old_line += 1; 331 current_old_line += 1;
237 current_new_line += 1; 332 current_new_line += 1;
238 } else if (line[0] == '+') { 333 } else if (line[0] == '+') {
239 deferred_new_add(line, current_new_line); 334 deferred_new_add(line, current_new_line);
240 current_new_line += 1; 335 current_new_line += 1;
241 } else if (line[0] == '-') { 336 } else if (line[0] == '-') {
242 deferred_old_add(line, current_old_line); 337 deferred_old_add(line, current_old_line);
243 current_old_line += 1; 338 current_old_line += 1;
244 } else if (line[0] == '@') { 339 } else if (line[0] == '@') {
245 html("<tr><td colspan='4' class='hunk'>"); 340 html("<tr><td colspan='4' class='hunk'>");
246 html_txt(line); 341 html_txt(line);
247 html("</td></tr>"); 342 html("</td></tr>");
248 } else { 343 } else {
249 html("<tr><td colspan='4' class='ctx'>"); 344 html("<tr><td colspan='4' class='ctx'>");
250 html_txt(line); 345 html_txt(line);
251 html("</td></tr>"); 346 html("</td></tr>");
252 } 347 }
253 line[len - 1] = c; 348 line[len - 1] = c;
254} 349}
255 350
256void cgit_ssdiff_header_begin() 351void cgit_ssdiff_header_begin()
257{ 352{
258 current_old_line = -1; 353 current_old_line = -1;
259 current_new_line = -1; 354 current_new_line = -1;
260 html("<tr><td class='space' colspan='4'><div></div></td></tr>"); 355 html("<tr><td class='space' colspan='4'><div></div></td></tr>");
261 html("<tr><td class='head' colspan='4'>"); 356 html("<tr><td class='head' colspan='4'>");
262} 357}
263 358
264void cgit_ssdiff_header_end() 359void cgit_ssdiff_header_end()
265{ 360{
266 html("</td><tr>"); 361 html("</td><tr>");
267} 362}
268 363
269void cgit_ssdiff_footer() 364void cgit_ssdiff_footer()
270{ 365{
271 if (deferred_old || deferred_new) 366 if (deferred_old || deferred_new)
272 cgit_ssdiff_print_deferred_lines(); 367 cgit_ssdiff_print_deferred_lines();
273 html("<tr><td class='foot' colspan='4'></td></tr>"); 368 html("<tr><td class='foot' colspan='4'></td></tr>");
274} 369}