##// END OF EJS Templates
bdiff: give slight preference to removing trailing lines...
Mads Kiilerich -
r30433:96f2f50d default
parent child Browse files
Show More
@@ -1,312 +1,312
1 /*
1 /*
2 bdiff.c - efficient binary diff extension for Mercurial
2 bdiff.c - efficient binary diff extension for Mercurial
3
3
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5
5
6 This software may be used and distributed according to the terms of
6 This software may be used and distributed according to the terms of
7 the GNU General Public License, incorporated herein by reference.
7 the GNU General Public License, incorporated herein by reference.
8
8
9 Based roughly on Python difflib
9 Based roughly on Python difflib
10 */
10 */
11
11
12 #include <stdlib.h>
12 #include <stdlib.h>
13 #include <string.h>
13 #include <string.h>
14 #include <limits.h>
14 #include <limits.h>
15
15
16 #include "compat.h"
16 #include "compat.h"
17 #include "bitmanipulation.h"
17 #include "bitmanipulation.h"
18 #include "bdiff.h"
18 #include "bdiff.h"
19
19
20 /* Hash implementation from diffutils */
20 /* Hash implementation from diffutils */
21 #define ROL(v, n) ((v) << (n) | (v) >> (sizeof(v) * CHAR_BIT - (n)))
21 #define ROL(v, n) ((v) << (n) | (v) >> (sizeof(v) * CHAR_BIT - (n)))
22 #define HASH(h, c) ((c) + ROL(h ,7))
22 #define HASH(h, c) ((c) + ROL(h ,7))
23
23
24 struct pos {
24 struct pos {
25 int pos, len;
25 int pos, len;
26 };
26 };
27
27
28 int bdiff_splitlines(const char *a, ssize_t len, struct bdiff_line **lr)
28 int bdiff_splitlines(const char *a, ssize_t len, struct bdiff_line **lr)
29 {
29 {
30 unsigned hash;
30 unsigned hash;
31 int i;
31 int i;
32 const char *p, *b = a;
32 const char *p, *b = a;
33 const char * const plast = a + len - 1;
33 const char * const plast = a + len - 1;
34 struct bdiff_line *l;
34 struct bdiff_line *l;
35
35
36 /* count the lines */
36 /* count the lines */
37 i = 1; /* extra line for sentinel */
37 i = 1; /* extra line for sentinel */
38 for (p = a; p < plast; p++)
38 for (p = a; p < plast; p++)
39 if (*p == '\n')
39 if (*p == '\n')
40 i++;
40 i++;
41 if (p == plast)
41 if (p == plast)
42 i++;
42 i++;
43
43
44 *lr = l = (struct bdiff_line *)malloc(sizeof(struct bdiff_line) * i);
44 *lr = l = (struct bdiff_line *)malloc(sizeof(struct bdiff_line) * i);
45 if (!l)
45 if (!l)
46 return -1;
46 return -1;
47
47
48 /* build the line array and calculate hashes */
48 /* build the line array and calculate hashes */
49 hash = 0;
49 hash = 0;
50 for (p = a; p < a + len; p++) {
50 for (p = a; p < a + len; p++) {
51 hash = HASH(hash, *p);
51 hash = HASH(hash, *p);
52
52
53 if (*p == '\n' || p == plast) {
53 if (*p == '\n' || p == plast) {
54 l->hash = hash;
54 l->hash = hash;
55 hash = 0;
55 hash = 0;
56 l->len = p - b + 1;
56 l->len = p - b + 1;
57 l->l = b;
57 l->l = b;
58 l->n = INT_MAX;
58 l->n = INT_MAX;
59 l++;
59 l++;
60 b = p + 1;
60 b = p + 1;
61 }
61 }
62 }
62 }
63
63
64 /* set up a sentinel */
64 /* set up a sentinel */
65 l->hash = 0;
65 l->hash = 0;
66 l->len = 0;
66 l->len = 0;
67 l->l = a + len;
67 l->l = a + len;
68 return i - 1;
68 return i - 1;
69 }
69 }
70
70
71 static inline int cmp(struct bdiff_line *a, struct bdiff_line *b)
71 static inline int cmp(struct bdiff_line *a, struct bdiff_line *b)
72 {
72 {
73 return a->hash != b->hash || a->len != b->len || memcmp(a->l, b->l, a->len);
73 return a->hash != b->hash || a->len != b->len || memcmp(a->l, b->l, a->len);
74 }
74 }
75
75
76 static int equatelines(struct bdiff_line *a, int an, struct bdiff_line *b,
76 static int equatelines(struct bdiff_line *a, int an, struct bdiff_line *b,
77 int bn)
77 int bn)
78 {
78 {
79 int i, j, buckets = 1, t, scale;
79 int i, j, buckets = 1, t, scale;
80 struct pos *h = NULL;
80 struct pos *h = NULL;
81
81
82 /* build a hash table of the next highest power of 2 */
82 /* build a hash table of the next highest power of 2 */
83 while (buckets < bn + 1)
83 while (buckets < bn + 1)
84 buckets *= 2;
84 buckets *= 2;
85
85
86 /* try to allocate a large hash table to avoid collisions */
86 /* try to allocate a large hash table to avoid collisions */
87 for (scale = 4; scale; scale /= 2) {
87 for (scale = 4; scale; scale /= 2) {
88 h = (struct pos *)malloc(scale * buckets * sizeof(struct pos));
88 h = (struct pos *)malloc(scale * buckets * sizeof(struct pos));
89 if (h)
89 if (h)
90 break;
90 break;
91 }
91 }
92
92
93 if (!h)
93 if (!h)
94 return 0;
94 return 0;
95
95
96 buckets = buckets * scale - 1;
96 buckets = buckets * scale - 1;
97
97
98 /* clear the hash table */
98 /* clear the hash table */
99 for (i = 0; i <= buckets; i++) {
99 for (i = 0; i <= buckets; i++) {
100 h[i].pos = -1;
100 h[i].pos = -1;
101 h[i].len = 0;
101 h[i].len = 0;
102 }
102 }
103
103
104 /* add lines to the hash table chains */
104 /* add lines to the hash table chains */
105 for (i = 0; i < bn; i++) {
105 for (i = 0; i < bn; i++) {
106 /* find the equivalence class */
106 /* find the equivalence class */
107 for (j = b[i].hash & buckets; h[j].pos != -1;
107 for (j = b[i].hash & buckets; h[j].pos != -1;
108 j = (j + 1) & buckets)
108 j = (j + 1) & buckets)
109 if (!cmp(b + i, b + h[j].pos))
109 if (!cmp(b + i, b + h[j].pos))
110 break;
110 break;
111
111
112 /* add to the head of the equivalence class */
112 /* add to the head of the equivalence class */
113 b[i].n = h[j].pos;
113 b[i].n = h[j].pos;
114 b[i].e = j;
114 b[i].e = j;
115 h[j].pos = i;
115 h[j].pos = i;
116 h[j].len++; /* keep track of popularity */
116 h[j].len++; /* keep track of popularity */
117 }
117 }
118
118
119 /* compute popularity threshold */
119 /* compute popularity threshold */
120 t = (bn >= 31000) ? bn / 1000 : 1000000 / (bn + 1);
120 t = (bn >= 31000) ? bn / 1000 : 1000000 / (bn + 1);
121
121
122 /* match items in a to their equivalence class in b */
122 /* match items in a to their equivalence class in b */
123 for (i = 0; i < an; i++) {
123 for (i = 0; i < an; i++) {
124 /* find the equivalence class */
124 /* find the equivalence class */
125 for (j = a[i].hash & buckets; h[j].pos != -1;
125 for (j = a[i].hash & buckets; h[j].pos != -1;
126 j = (j + 1) & buckets)
126 j = (j + 1) & buckets)
127 if (!cmp(a + i, b + h[j].pos))
127 if (!cmp(a + i, b + h[j].pos))
128 break;
128 break;
129
129
130 a[i].e = j; /* use equivalence class for quick compare */
130 a[i].e = j; /* use equivalence class for quick compare */
131 if (h[j].len <= t)
131 if (h[j].len <= t)
132 a[i].n = h[j].pos; /* point to head of match list */
132 a[i].n = h[j].pos; /* point to head of match list */
133 else
133 else
134 a[i].n = -1; /* too popular */
134 a[i].n = -1; /* too popular */
135 }
135 }
136
136
137 /* discard hash tables */
137 /* discard hash tables */
138 free(h);
138 free(h);
139 return 1;
139 return 1;
140 }
140 }
141
141
142 static int longest_match(struct bdiff_line *a, struct bdiff_line *b,
142 static int longest_match(struct bdiff_line *a, struct bdiff_line *b,
143 struct pos *pos,
143 struct pos *pos,
144 int a1, int a2, int b1, int b2, int *omi, int *omj)
144 int a1, int a2, int b1, int b2, int *omi, int *omj)
145 {
145 {
146 int mi = a1, mj = b1, mk = 0, i, j, k, half, bhalf;
146 int mi = a1, mj = b1, mk = 0, i, j, k, half, bhalf;
147
147
148 /* window our search on large regions to better bound
148 /* window our search on large regions to better bound
149 worst-case performance. by choosing a window at the end, we
149 worst-case performance. by choosing a window at the end, we
150 reduce skipping overhead on the b chains. */
150 reduce skipping overhead on the b chains. */
151 if (a2 - a1 > 30000)
151 if (a2 - a1 > 30000)
152 a1 = a2 - 30000;
152 a1 = a2 - 30000;
153
153
154 half = (a1 + a2 - 1) / 2;
154 half = (a1 + a2 - 1) / 2;
155 bhalf = (b1 + b2 - 1) / 2;
155 bhalf = (b1 + b2 - 1) / 2;
156
156
157 for (i = a1; i < a2; i++) {
157 for (i = a1; i < a2; i++) {
158 /* skip all lines in b after the current block */
158 /* skip all lines in b after the current block */
159 for (j = a[i].n; j >= b2; j = b[j].n)
159 for (j = a[i].n; j >= b2; j = b[j].n)
160 ;
160 ;
161
161
162 /* loop through all lines match a[i] in b */
162 /* loop through all lines match a[i] in b */
163 for (; j >= b1; j = b[j].n) {
163 for (; j >= b1; j = b[j].n) {
164 /* does this extend an earlier match? */
164 /* does this extend an earlier match? */
165 for (k = 1; j - k >= b1 && i - k >= a1; k++) {
165 for (k = 1; j - k >= b1 && i - k >= a1; k++) {
166 /* reached an earlier match? */
166 /* reached an earlier match? */
167 if (pos[j - k].pos == i - k) {
167 if (pos[j - k].pos == i - k) {
168 k += pos[j - k].len;
168 k += pos[j - k].len;
169 break;
169 break;
170 }
170 }
171 /* previous line mismatch? */
171 /* previous line mismatch? */
172 if (a[i - k].e != b[j - k].e)
172 if (a[i - k].e != b[j - k].e)
173 break;
173 break;
174 }
174 }
175
175
176 pos[j].pos = i;
176 pos[j].pos = i;
177 pos[j].len = k;
177 pos[j].len = k;
178
178
179 /* best match so far? we prefer matches closer
179 /* best match so far? we prefer matches closer
180 to the middle to balance recursion */
180 to the middle to balance recursion */
181 if (k > mk) {
181 if (k > mk) {
182 /* a longer match */
182 /* a longer match */
183 mi = i;
183 mi = i;
184 mj = j;
184 mj = j;
185 mk = k;
185 mk = k;
186 } else if (k == mk) {
186 } else if (k == mk) {
187 if (i > mi && i <= half) {
187 if (i > mi && i <= half && j > b1) {
188 /* same match but closer to half */
188 /* same match but closer to half */
189 mi = i;
189 mi = i;
190 mj = j;
190 mj = j;
191 } else if (i == mi && (mj > bhalf || i == a1)) {
191 } else if (i == mi && (mj > bhalf || i == a1)) {
192 /* same i but best earlier j */
192 /* same i but best earlier j */
193 mj = j;
193 mj = j;
194 }
194 }
195 }
195 }
196 }
196 }
197 }
197 }
198
198
199 if (mk) {
199 if (mk) {
200 mi = mi - mk + 1;
200 mi = mi - mk + 1;
201 mj = mj - mk + 1;
201 mj = mj - mk + 1;
202 }
202 }
203
203
204 /* expand match to include subsequent popular lines */
204 /* expand match to include subsequent popular lines */
205 while (mi + mk < a2 && mj + mk < b2 &&
205 while (mi + mk < a2 && mj + mk < b2 &&
206 a[mi + mk].e == b[mj + mk].e)
206 a[mi + mk].e == b[mj + mk].e)
207 mk++;
207 mk++;
208
208
209 *omi = mi;
209 *omi = mi;
210 *omj = mj;
210 *omj = mj;
211
211
212 return mk;
212 return mk;
213 }
213 }
214
214
215 static struct bdiff_hunk *recurse(struct bdiff_line *a, struct bdiff_line *b,
215 static struct bdiff_hunk *recurse(struct bdiff_line *a, struct bdiff_line *b,
216 struct pos *pos,
216 struct pos *pos,
217 int a1, int a2, int b1, int b2, struct bdiff_hunk *l)
217 int a1, int a2, int b1, int b2, struct bdiff_hunk *l)
218 {
218 {
219 int i, j, k;
219 int i, j, k;
220
220
221 while (1) {
221 while (1) {
222 /* find the longest match in this chunk */
222 /* find the longest match in this chunk */
223 k = longest_match(a, b, pos, a1, a2, b1, b2, &i, &j);
223 k = longest_match(a, b, pos, a1, a2, b1, b2, &i, &j);
224 if (!k)
224 if (!k)
225 return l;
225 return l;
226
226
227 /* and recurse on the remaining chunks on either side */
227 /* and recurse on the remaining chunks on either side */
228 l = recurse(a, b, pos, a1, i, b1, j, l);
228 l = recurse(a, b, pos, a1, i, b1, j, l);
229 if (!l)
229 if (!l)
230 return NULL;
230 return NULL;
231
231
232 l->next = (struct bdiff_hunk *)malloc(sizeof(struct bdiff_hunk));
232 l->next = (struct bdiff_hunk *)malloc(sizeof(struct bdiff_hunk));
233 if (!l->next)
233 if (!l->next)
234 return NULL;
234 return NULL;
235
235
236 l = l->next;
236 l = l->next;
237 l->a1 = i;
237 l->a1 = i;
238 l->a2 = i + k;
238 l->a2 = i + k;
239 l->b1 = j;
239 l->b1 = j;
240 l->b2 = j + k;
240 l->b2 = j + k;
241 l->next = NULL;
241 l->next = NULL;
242
242
243 /* tail-recursion didn't happen, so do equivalent iteration */
243 /* tail-recursion didn't happen, so do equivalent iteration */
244 a1 = i + k;
244 a1 = i + k;
245 b1 = j + k;
245 b1 = j + k;
246 }
246 }
247 }
247 }
248
248
249 int bdiff_diff(struct bdiff_line *a, int an, struct bdiff_line *b,
249 int bdiff_diff(struct bdiff_line *a, int an, struct bdiff_line *b,
250 int bn, struct bdiff_hunk *base)
250 int bn, struct bdiff_hunk *base)
251 {
251 {
252 struct bdiff_hunk *curr;
252 struct bdiff_hunk *curr;
253 struct pos *pos;
253 struct pos *pos;
254 int t, count = 0;
254 int t, count = 0;
255
255
256 /* allocate and fill arrays */
256 /* allocate and fill arrays */
257 t = equatelines(a, an, b, bn);
257 t = equatelines(a, an, b, bn);
258 pos = (struct pos *)calloc(bn ? bn : 1, sizeof(struct pos));
258 pos = (struct pos *)calloc(bn ? bn : 1, sizeof(struct pos));
259
259
260 if (pos && t) {
260 if (pos && t) {
261 /* generate the matching block list */
261 /* generate the matching block list */
262
262
263 curr = recurse(a, b, pos, 0, an, 0, bn, base);
263 curr = recurse(a, b, pos, 0, an, 0, bn, base);
264 if (!curr)
264 if (!curr)
265 return -1;
265 return -1;
266
266
267 /* sentinel end hunk */
267 /* sentinel end hunk */
268 curr->next = (struct bdiff_hunk *)malloc(sizeof(struct bdiff_hunk));
268 curr->next = (struct bdiff_hunk *)malloc(sizeof(struct bdiff_hunk));
269 if (!curr->next)
269 if (!curr->next)
270 return -1;
270 return -1;
271 curr = curr->next;
271 curr = curr->next;
272 curr->a1 = curr->a2 = an;
272 curr->a1 = curr->a2 = an;
273 curr->b1 = curr->b2 = bn;
273 curr->b1 = curr->b2 = bn;
274 curr->next = NULL;
274 curr->next = NULL;
275 }
275 }
276
276
277 free(pos);
277 free(pos);
278
278
279 /* normalize the hunk list, try to push each hunk towards the end */
279 /* normalize the hunk list, try to push each hunk towards the end */
280 for (curr = base->next; curr; curr = curr->next) {
280 for (curr = base->next; curr; curr = curr->next) {
281 struct bdiff_hunk *next = curr->next;
281 struct bdiff_hunk *next = curr->next;
282
282
283 if (!next)
283 if (!next)
284 break;
284 break;
285
285
286 if (curr->a2 == next->a1 || curr->b2 == next->b1)
286 if (curr->a2 == next->a1 || curr->b2 == next->b1)
287 while (curr->a2 < an && curr->b2 < bn
287 while (curr->a2 < an && curr->b2 < bn
288 && next->a1 < next->a2
288 && next->a1 < next->a2
289 && next->b1 < next->b2
289 && next->b1 < next->b2
290 && !cmp(a + curr->a2, b + curr->b2)) {
290 && !cmp(a + curr->a2, b + curr->b2)) {
291 curr->a2++;
291 curr->a2++;
292 next->a1++;
292 next->a1++;
293 curr->b2++;
293 curr->b2++;
294 next->b1++;
294 next->b1++;
295 }
295 }
296 }
296 }
297
297
298 for (curr = base->next; curr; curr = curr->next)
298 for (curr = base->next; curr; curr = curr->next)
299 count++;
299 count++;
300 return count;
300 return count;
301 }
301 }
302
302
303 void bdiff_freehunks(struct bdiff_hunk *l)
303 void bdiff_freehunks(struct bdiff_hunk *l)
304 {
304 {
305 struct bdiff_hunk *n;
305 struct bdiff_hunk *n;
306 for (; l; l = n) {
306 for (; l; l = n) {
307 n = l->next;
307 n = l->next;
308 free(l);
308 free(l);
309 }
309 }
310 }
310 }
311
311
312
312
@@ -1,94 +1,94
1 from __future__ import absolute_import, print_function
1 from __future__ import absolute_import, print_function
2 import struct
2 import struct
3 from mercurial import (
3 from mercurial import (
4 bdiff,
4 bdiff,
5 mpatch,
5 mpatch,
6 )
6 )
7
7
8 def test1(a, b):
8 def test1(a, b):
9 d = bdiff.bdiff(a, b)
9 d = bdiff.bdiff(a, b)
10 c = a
10 c = a
11 if d:
11 if d:
12 c = mpatch.patches(a, [d])
12 c = mpatch.patches(a, [d])
13 if c != b:
13 if c != b:
14 print("bad diff+patch result from\n %r to\n %r:" % (a, b))
14 print("bad diff+patch result from\n %r to\n %r:" % (a, b))
15 print("bdiff: %r" % d)
15 print("bdiff: %r" % d)
16 print("patched: %r" % c[:200])
16 print("patched: %r" % c[:200])
17
17
18 def test(a, b):
18 def test(a, b):
19 print("test", repr(a), repr(b))
19 print("test", repr(a), repr(b))
20 test1(a, b)
20 test1(a, b)
21 test1(b, a)
21 test1(b, a)
22
22
23 test("a\nc\n\n\n\n", "a\nb\n\n\n")
23 test("a\nc\n\n\n\n", "a\nb\n\n\n")
24 test("a\nb\nc\n", "a\nc\n")
24 test("a\nb\nc\n", "a\nc\n")
25 test("", "")
25 test("", "")
26 test("a\nb\nc", "a\nb\nc")
26 test("a\nb\nc", "a\nb\nc")
27 test("a\nb\nc\nd\n", "a\nd\n")
27 test("a\nb\nc\nd\n", "a\nd\n")
28 test("a\nb\nc\nd\n", "a\nc\ne\n")
28 test("a\nb\nc\nd\n", "a\nc\ne\n")
29 test("a\nb\nc\n", "a\nc\n")
29 test("a\nb\nc\n", "a\nc\n")
30 test("a\n", "c\na\nb\n")
30 test("a\n", "c\na\nb\n")
31 test("a\n", "")
31 test("a\n", "")
32 test("a\n", "b\nc\n")
32 test("a\n", "b\nc\n")
33 test("a\n", "c\na\n")
33 test("a\n", "c\na\n")
34 test("", "adjfkjdjksdhfksj")
34 test("", "adjfkjdjksdhfksj")
35 test("", "ab")
35 test("", "ab")
36 test("", "abc")
36 test("", "abc")
37 test("a", "a")
37 test("a", "a")
38 test("ab", "ab")
38 test("ab", "ab")
39 test("abc", "abc")
39 test("abc", "abc")
40 test("a\n", "a\n")
40 test("a\n", "a\n")
41 test("a\nb", "a\nb")
41 test("a\nb", "a\nb")
42
42
43 #issue1295
43 #issue1295
44 def showdiff(a, b):
44 def showdiff(a, b):
45 print('showdiff(\n %r,\n %r):' % (a, b))
45 print('showdiff(\n %r,\n %r):' % (a, b))
46 bin = bdiff.bdiff(a, b)
46 bin = bdiff.bdiff(a, b)
47 pos = 0
47 pos = 0
48 q = 0
48 q = 0
49 while pos < len(bin):
49 while pos < len(bin):
50 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
50 p1, p2, l = struct.unpack(">lll", bin[pos:pos + 12])
51 pos += 12
51 pos += 12
52 if p1:
52 if p1:
53 print('', repr(a[q:p1]))
53 print('', repr(a[q:p1]))
54 print('', p1, p2, repr(a[p1:p2]), '->', repr(bin[pos:pos + l]))
54 print('', p1, p2, repr(a[p1:p2]), '->', repr(bin[pos:pos + l]))
55 pos += l
55 pos += l
56 q = p2
56 q = p2
57 if q < len(a):
57 if q < len(a):
58 print('', repr(a[q:]))
58 print('', repr(a[q:]))
59
59
60 showdiff("x\n\nx\n\nx\n\nx\n\nz\n", "x\n\nx\n\ny\n\nx\n\nx\n\nz\n")
60 showdiff("x\n\nx\n\nx\n\nx\n\nz\n", "x\n\nx\n\ny\n\nx\n\nx\n\nz\n")
61 showdiff("x\n\nx\n\nx\n\nx\n\nz\n", "x\n\nx\n\ny\n\nx\n\ny\n\nx\n\nz\n")
61 showdiff("x\n\nx\n\nx\n\nx\n\nz\n", "x\n\nx\n\ny\n\nx\n\ny\n\nx\n\nz\n")
62 # we should pick up abbbc. rather than bc.de as the longest match
62 # we should pick up abbbc. rather than bc.de as the longest match
63 showdiff("a\nb\nb\nb\nc\n.\nd\ne\n.\nf\n",
63 showdiff("a\nb\nb\nb\nc\n.\nd\ne\n.\nf\n",
64 "a\nb\nb\na\nb\nb\nb\nc\n.\nb\nc\n.\nd\ne\nf\n")
64 "a\nb\nb\na\nb\nb\nb\nc\n.\nb\nc\n.\nd\ne\nf\n")
65
65
66 print("done")
66 print("done")
67
67
68 def testfixws(a, b, allws):
68 def testfixws(a, b, allws):
69 c = bdiff.fixws(a, allws)
69 c = bdiff.fixws(a, allws)
70 if c != b:
70 if c != b:
71 print("*** fixws", repr(a), repr(b), allws)
71 print("*** fixws", repr(a), repr(b), allws)
72 print("got:")
72 print("got:")
73 print(repr(c))
73 print(repr(c))
74
74
75 testfixws(" \ta\r b\t\n", "ab\n", 1)
75 testfixws(" \ta\r b\t\n", "ab\n", 1)
76 testfixws(" \ta\r b\t\n", " a b\n", 0)
76 testfixws(" \ta\r b\t\n", " a b\n", 0)
77 testfixws("", "", 1)
77 testfixws("", "", 1)
78 testfixws("", "", 0)
78 testfixws("", "", 0)
79
79
80 print("done")
80 print("done")
81
81
82 print("Nice diff for a trivial change:")
82 print("Nice diff for a trivial change:")
83 showdiff(
83 showdiff(
84 ''.join('<%s\n-\n' % i for i in range(5)),
84 ''.join('<%s\n-\n' % i for i in range(5)),
85 ''.join('>%s\n-\n' % i for i in range(5)))
85 ''.join('>%s\n-\n' % i for i in range(5)))
86
86
87 print("Diff 1 to 3 lines - preference for appending:")
87 print("Diff 1 to 3 lines - preference for appending:")
88 showdiff('a\n', 'a\n' * 3)
88 showdiff('a\n', 'a\n' * 3)
89 print("Diff 1 to 5 lines - preference for appending:")
89 print("Diff 1 to 5 lines - preference for appending:")
90 showdiff('a\n', 'a\n' * 5)
90 showdiff('a\n', 'a\n' * 5)
91 print("Diff 3 to 1 lines - preference for balanced recursion:")
91 print("Diff 3 to 1 lines - preference for removing trailing lines:")
92 showdiff('a\n' * 3, 'a\n')
92 showdiff('a\n' * 3, 'a\n')
93 print("Diff 5 to 1 lines - preference for balanced recursion:")
93 print("Diff 5 to 1 lines - preference for removing trailing lines:")
94 showdiff('a\n' * 5, 'a\n')
94 showdiff('a\n' * 5, 'a\n')
@@ -1,84 +1,82
1 test 'a\nc\n\n\n\n' 'a\nb\n\n\n'
1 test 'a\nc\n\n\n\n' 'a\nb\n\n\n'
2 test 'a\nb\nc\n' 'a\nc\n'
2 test 'a\nb\nc\n' 'a\nc\n'
3 test '' ''
3 test '' ''
4 test 'a\nb\nc' 'a\nb\nc'
4 test 'a\nb\nc' 'a\nb\nc'
5 test 'a\nb\nc\nd\n' 'a\nd\n'
5 test 'a\nb\nc\nd\n' 'a\nd\n'
6 test 'a\nb\nc\nd\n' 'a\nc\ne\n'
6 test 'a\nb\nc\nd\n' 'a\nc\ne\n'
7 test 'a\nb\nc\n' 'a\nc\n'
7 test 'a\nb\nc\n' 'a\nc\n'
8 test 'a\n' 'c\na\nb\n'
8 test 'a\n' 'c\na\nb\n'
9 test 'a\n' ''
9 test 'a\n' ''
10 test 'a\n' 'b\nc\n'
10 test 'a\n' 'b\nc\n'
11 test 'a\n' 'c\na\n'
11 test 'a\n' 'c\na\n'
12 test '' 'adjfkjdjksdhfksj'
12 test '' 'adjfkjdjksdhfksj'
13 test '' 'ab'
13 test '' 'ab'
14 test '' 'abc'
14 test '' 'abc'
15 test 'a' 'a'
15 test 'a' 'a'
16 test 'ab' 'ab'
16 test 'ab' 'ab'
17 test 'abc' 'abc'
17 test 'abc' 'abc'
18 test 'a\n' 'a\n'
18 test 'a\n' 'a\n'
19 test 'a\nb' 'a\nb'
19 test 'a\nb' 'a\nb'
20 showdiff(
20 showdiff(
21 'x\n\nx\n\nx\n\nx\n\nz\n',
21 'x\n\nx\n\nx\n\nx\n\nz\n',
22 'x\n\nx\n\ny\n\nx\n\nx\n\nz\n'):
22 'x\n\nx\n\ny\n\nx\n\nx\n\nz\n'):
23 'x\n\nx\n\n'
23 'x\n\nx\n\n'
24 6 6 '' -> 'y\n\n'
24 6 6 '' -> 'y\n\n'
25 'x\n\nx\n\nz\n'
25 'x\n\nx\n\nz\n'
26 showdiff(
26 showdiff(
27 'x\n\nx\n\nx\n\nx\n\nz\n',
27 'x\n\nx\n\nx\n\nx\n\nz\n',
28 'x\n\nx\n\ny\n\nx\n\ny\n\nx\n\nz\n'):
28 'x\n\nx\n\ny\n\nx\n\ny\n\nx\n\nz\n'):
29 'x\n\nx\n\n'
29 'x\n\nx\n\n'
30 6 6 '' -> 'y\n\n'
30 6 6 '' -> 'y\n\n'
31 'x\n\n'
31 'x\n\n'
32 9 9 '' -> 'y\n\n'
32 9 9 '' -> 'y\n\n'
33 'x\n\nz\n'
33 'x\n\nz\n'
34 showdiff(
34 showdiff(
35 'a\nb\nb\nb\nc\n.\nd\ne\n.\nf\n',
35 'a\nb\nb\nb\nc\n.\nd\ne\n.\nf\n',
36 'a\nb\nb\na\nb\nb\nb\nc\n.\nb\nc\n.\nd\ne\nf\n'):
36 'a\nb\nb\na\nb\nb\nb\nc\n.\nb\nc\n.\nd\ne\nf\n'):
37 0 0 '' -> 'a\nb\nb\n'
37 0 0 '' -> 'a\nb\nb\n'
38 'a\nb\nb\nb\nc\n.\n'
38 'a\nb\nb\nb\nc\n.\n'
39 12 12 '' -> 'b\nc\n.\n'
39 12 12 '' -> 'b\nc\n.\n'
40 'd\ne\n'
40 'd\ne\n'
41 16 18 '.\n' -> ''
41 16 18 '.\n' -> ''
42 'f\n'
42 'f\n'
43 done
43 done
44 done
44 done
45 Nice diff for a trivial change:
45 Nice diff for a trivial change:
46 showdiff(
46 showdiff(
47 '<0\n-\n<1\n-\n<2\n-\n<3\n-\n<4\n-\n',
47 '<0\n-\n<1\n-\n<2\n-\n<3\n-\n<4\n-\n',
48 '>0\n-\n>1\n-\n>2\n-\n>3\n-\n>4\n-\n'):
48 '>0\n-\n>1\n-\n>2\n-\n>3\n-\n>4\n-\n'):
49 0 3 '<0\n' -> '>0\n'
49 0 3 '<0\n' -> '>0\n'
50 '-\n'
50 '-\n'
51 5 8 '<1\n' -> '>1\n'
51 5 8 '<1\n' -> '>1\n'
52 '-\n'
52 '-\n'
53 10 13 '<2\n' -> '>2\n'
53 10 13 '<2\n' -> '>2\n'
54 '-\n'
54 '-\n'
55 15 18 '<3\n' -> '>3\n'
55 15 18 '<3\n' -> '>3\n'
56 '-\n'
56 '-\n'
57 20 23 '<4\n' -> '>4\n'
57 20 23 '<4\n' -> '>4\n'
58 '-\n'
58 '-\n'
59 Diff 1 to 3 lines - preference for appending:
59 Diff 1 to 3 lines - preference for appending:
60 showdiff(
60 showdiff(
61 'a\n',
61 'a\n',
62 'a\na\na\n'):
62 'a\na\na\n'):
63 'a\n'
63 'a\n'
64 2 2 '' -> 'a\na\n'
64 2 2 '' -> 'a\na\n'
65 Diff 1 to 5 lines - preference for appending:
65 Diff 1 to 5 lines - preference for appending:
66 showdiff(
66 showdiff(
67 'a\n',
67 'a\n',
68 'a\na\na\na\na\n'):
68 'a\na\na\na\na\n'):
69 'a\n'
69 'a\n'
70 2 2 '' -> 'a\na\na\na\n'
70 2 2 '' -> 'a\na\na\na\n'
71 Diff 3 to 1 lines - preference for balanced recursion:
71 Diff 3 to 1 lines - preference for removing trailing lines:
72 showdiff(
72 showdiff(
73 'a\na\na\n',
73 'a\na\na\n',
74 'a\n'):
74 'a\n'):
75 0 2 'a\n' -> ''
76 'a\n'
75 'a\n'
77 4 6 'a\n' -> ''
76 2 6 'a\na\n' -> ''
78 Diff 5 to 1 lines - preference for balanced recursion:
77 Diff 5 to 1 lines - preference for removing trailing lines:
79 showdiff(
78 showdiff(
80 'a\na\na\na\na\n',
79 'a\na\na\na\na\n',
81 'a\n'):
80 'a\n'):
82 0 4 'a\na\n' -> ''
83 'a\n'
81 'a\n'
84 6 10 'a\na\n' -> ''
82 2 10 'a\na\na\na\n' -> ''
General Comments 0
You need to be logged in to leave comments. Login now