47 return sa, sb |
47 return sa, sb |
48 else: |
48 else: |
49 return None |
49 return None |
50 |
50 |
51 |
51 |
|
52 def intersect_or_touch(ra, rb): |
|
53 """Given two ranges return the range where they intersect or touch or None. |
|
54 |
|
55 >>> intersect_or_touch((0, 10), (0, 6)) |
|
56 (0, 6) |
|
57 >>> intersect_or_touch((0, 10), (5, 15)) |
|
58 (5, 10) |
|
59 >>> intersect_or_touch((0, 10), (10, 15)) |
|
60 (10, 10) |
|
61 >>> intersect_or_touch((0, 9), (10, 15)) |
|
62 >>> intersect_or_touch((0, 9), (7, 15)) |
|
63 (7, 9) |
|
64 """ |
|
65 assert ra[0] <= ra[1] |
|
66 assert rb[0] <= rb[1] |
|
67 |
|
68 sa = max(ra[0], rb[0]) |
|
69 sb = min(ra[1], rb[1]) |
|
70 if sa <= sb: |
|
71 return sa, sb |
|
72 else: |
|
73 return None |
|
74 |
|
75 |
52 def compare_range(a, astart, aend, b, bstart, bend): |
76 def compare_range(a, astart, aend, b, bstart, bend): |
53 """Compare a[astart:aend] == b[bstart:bend], without slicing.""" |
77 """Compare a[astart:aend] == b[bstart:bend], without slicing.""" |
54 if (aend - astart) != (bend - bstart): |
78 if (aend - astart) != (bend - bstart): |
55 return False |
79 return False |
56 for ia, ib in zip(range(astart, aend), range(bstart, bend)): |
80 for ia, ib in zip(range(astart, aend), range(bstart, bend)): |
64 """3-way merge of texts. |
88 """3-way merge of texts. |
65 |
89 |
66 Given strings BASE, OTHER, THIS, tries to produce a combined text |
90 Given strings BASE, OTHER, THIS, tries to produce a combined text |
67 incorporating the changes from both BASE->OTHER and BASE->THIS.""" |
91 incorporating the changes from both BASE->OTHER and BASE->THIS.""" |
68 |
92 |
69 def __init__(self, basetext, atext, btext, base=None, a=None, b=None): |
93 def __init__( |
|
94 self, |
|
95 basetext, |
|
96 atext, |
|
97 btext, |
|
98 base=None, |
|
99 a=None, |
|
100 b=None, |
|
101 relaxed_sync=False, |
|
102 ): |
70 self.basetext = basetext |
103 self.basetext = basetext |
71 self.atext = atext |
104 self.atext = atext |
72 self.btext = btext |
105 self.btext = btext |
73 if base is None: |
106 if base is None: |
74 base = mdiff.splitnewlines(basetext) |
107 base = mdiff.splitnewlines(basetext) |
75 if a is None: |
108 if a is None: |
76 a = mdiff.splitnewlines(atext) |
109 a = mdiff.splitnewlines(atext) |
77 if b is None: |
110 if b is None: |
78 b = mdiff.splitnewlines(btext) |
111 b = mdiff.splitnewlines(btext) |
|
112 self.relaxed_sync = relaxed_sync |
79 self.base = base |
113 self.base = base |
80 self.a = a |
114 self.a = a |
81 self.b = b |
115 self.b = b |
82 |
116 |
83 def merge_groups(self): |
117 def merge_groups(self): |
218 amatches = mdiff.get_matching_blocks(self.basetext, self.atext) |
252 amatches = mdiff.get_matching_blocks(self.basetext, self.atext) |
219 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext) |
253 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext) |
220 len_a = len(amatches) |
254 len_a = len(amatches) |
221 len_b = len(bmatches) |
255 len_b = len(bmatches) |
222 |
256 |
|
257 if self.relaxed_sync: |
|
258 intersect_fun = intersect_or_touch |
|
259 else: |
|
260 intersect_fun = intersect |
|
261 |
223 sl = [] |
262 sl = [] |
224 |
263 |
225 while ia < len_a and ib < len_b: |
264 while ia < len_a and ib < len_b: |
226 abase, amatch, alen = amatches[ia] |
265 abase, amatch, alen = amatches[ia] |
227 bbase, bmatch, blen = bmatches[ib] |
266 bbase, bmatch, blen = bmatches[ib] |
228 |
267 |
229 # there is an unconflicted block at i; how long does it |
268 # there is an unconflicted block at i; how long does it |
230 # extend? until whichever one ends earlier. |
269 # extend? until whichever one ends earlier. |
231 i = intersect((abase, abase + alen), (bbase, bbase + blen)) |
270 i = intersect_fun((abase, abase + alen), (bbase, bbase + blen)) |
232 if i: |
271 if i: |
233 intbase = i[0] |
272 intbase = i[0] |
234 intend = i[1] |
273 intend = i[1] |
235 intlen = intend - intbase |
274 intlen = intend - intbase |
236 |
275 |
256 sl.append((intbase, intend, asub, aend, bsub, bend)) |
295 sl.append((intbase, intend, asub, aend, bsub, bend)) |
257 |
296 |
258 # advance whichever one ends first in the base text |
297 # advance whichever one ends first in the base text |
259 if (abase + alen) < (bbase + blen): |
298 if (abase + alen) < (bbase + blen): |
260 ia += 1 |
299 ia += 1 |
|
300 elif not self.relaxed_sync: |
|
301 # if the blocks end at the same time we know they can't overlap |
|
302 # any other block, so no need for the complicated checks below |
|
303 ib += 1 |
|
304 elif (abase + alen) > (bbase + blen): |
|
305 ib += 1 |
261 else: |
306 else: |
262 ib += 1 |
307 # If both end at the same time, either may touching the |
|
308 # follow-up matching block on the other side. |
|
309 # Advance the one whose next block comes sooner. |
|
310 if ia + 1 == len_a: |
|
311 # if we run out of blocks on A side, we may as well advance B |
|
312 # since there's nothing on A side for that to touch |
|
313 ib += 1 |
|
314 elif ib + 1 == len_b: |
|
315 ia += 1 |
|
316 elif amatches[ia + 1][0] > bmatches[ib + 1][0]: |
|
317 ib += 1 |
|
318 elif amatches[ia + 1][0] < bmatches[ib + 1][0]: |
|
319 ia += 1 |
|
320 else: |
|
321 # symmetric situation: both sides added lines to the same place |
|
322 # it's less surprising if we treat it as a conflict, so skip |
|
323 # both without a preferred order |
|
324 ia += 1 |
|
325 ib += 1 |
263 |
326 |
264 intbase = len(self.base) |
327 intbase = len(self.base) |
265 abase = len(self.a) |
328 abase = len(self.a) |
266 bbase = len(self.b) |
329 bbase = len(self.b) |
267 sl.append((intbase, intbase, abase, abase, bbase, bbase)) |
330 sentinel_hunk = (intbase, intbase, abase, abase, bbase, bbase) |
|
331 # we avoid duplicate sentinel hunk at the end to make the |
|
332 # test output cleaner |
|
333 if not (sl and sl[len(sl) - 1] == sentinel_hunk): |
|
334 sl.append(sentinel_hunk) |
268 |
335 |
269 return sl |
336 return sl |
270 |
337 |
271 |
338 |
272 def _verifytext(input): |
339 def _verifytext(input): |
507 if not allow_binary: |
575 if not allow_binary: |
508 _verifytext(local) |
576 _verifytext(local) |
509 _verifytext(base) |
577 _verifytext(base) |
510 _verifytext(other) |
578 _verifytext(other) |
511 |
579 |
512 m3 = Merge3Text(base.text(), local.text(), other.text()) |
580 m3 = Merge3Text( |
|
581 base.text(), local.text(), other.text(), relaxed_sync=relaxed_sync |
|
582 ) |
513 conflicts = False |
583 conflicts = False |
514 if mode == b'union': |
584 if mode == b'union': |
515 lines = _resolve(m3, (1, 2)) |
585 lines = _resolve(m3, (1, 2)) |
516 elif mode == b'union-other-first': |
586 elif mode == b'union-other-first': |
517 lines = _resolve(m3, (2, 1)) |
587 lines = _resolve(m3, (2, 1)) |