48 >>> intersect((0, 9), (7, 15)) |
48 >>> intersect((0, 9), (7, 15)) |
49 (7, 9) |
49 (7, 9) |
50 """ |
50 """ |
51 assert ra[0] <= ra[1] |
51 assert ra[0] <= ra[1] |
52 assert rb[0] <= rb[1] |
52 assert rb[0] <= rb[1] |
53 |
53 |
54 sa = max(ra[0], rb[0]) |
54 sa = max(ra[0], rb[0]) |
55 sb = min(ra[1], rb[1]) |
55 sb = min(ra[1], rb[1]) |
56 if sa < sb: |
56 if sa < sb: |
57 return sa, sb |
57 return sa, sb |
58 else: |
58 else: |
147 for i in range(t[5], t[6]): |
147 for i in range(t[5], t[6]): |
148 yield self.b[i] |
148 yield self.b[i] |
149 yield end_marker + newline |
149 yield end_marker + newline |
150 else: |
150 else: |
151 raise ValueError(what) |
151 raise ValueError(what) |
152 |
152 |
153 |
153 |
154 |
154 |
155 |
155 |
156 |
156 |
157 def merge_annotated(self): |
157 def merge_annotated(self): |
158 """Return merge with conflicts, showing origin of lines. |
158 """Return merge with conflicts, showing origin of lines. |
159 |
159 |
160 Most useful for debugging merge. |
160 Most useful for debugging merge. |
161 """ |
161 """ |
162 for t in self.merge_regions(): |
162 for t in self.merge_regions(): |
163 what = t[0] |
163 what = t[0] |
164 if what == 'unchanged': |
164 if what == 'unchanged': |
165 for i in range(t[1], t[2]): |
165 for i in range(t[1], t[2]): |
178 for i in range(t[5], t[6]): |
178 for i in range(t[5], t[6]): |
179 yield 'B | ' + self.b[i] |
179 yield 'B | ' + self.b[i] |
180 yield '>>>>\n' |
180 yield '>>>>\n' |
181 else: |
181 else: |
182 raise ValueError(what) |
182 raise ValueError(what) |
183 |
183 |
184 |
184 |
185 |
185 |
186 |
186 |
187 |
187 |
188 def merge_groups(self): |
188 def merge_groups(self): |
189 """Yield sequence of line groups. Each one is a tuple: |
189 """Yield sequence of line groups. Each one is a tuple: |
247 conflicted, or changed on only one side. |
247 conflicted, or changed on only one side. |
248 """ |
248 """ |
249 |
249 |
250 # section a[0:ia] has been disposed of, etc |
250 # section a[0:ia] has been disposed of, etc |
251 iz = ia = ib = 0 |
251 iz = ia = ib = 0 |
252 |
252 |
253 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions(): |
253 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions(): |
254 #print 'match base [%d:%d]' % (zmatch, zend) |
254 #print 'match base [%d:%d]' % (zmatch, zend) |
255 |
255 |
256 matchlen = zend - zmatch |
256 matchlen = zend - zmatch |
257 assert matchlen >= 0 |
257 assert matchlen >= 0 |
258 assert matchlen == (aend - amatch) |
258 assert matchlen == (aend - amatch) |
259 assert matchlen == (bend - bmatch) |
259 assert matchlen == (bend - bmatch) |
260 |
260 |
261 len_a = amatch - ia |
261 len_a = amatch - ia |
262 len_b = bmatch - ib |
262 len_b = bmatch - ib |
263 len_base = zmatch - iz |
263 len_base = zmatch - iz |
264 assert len_a >= 0 |
264 assert len_a >= 0 |
265 assert len_b >= 0 |
265 assert len_b >= 0 |
292 iz = zmatch |
292 iz = zmatch |
293 |
293 |
294 # if the same part of the base was deleted on both sides |
294 # if the same part of the base was deleted on both sides |
295 # that's OK, we can just skip it. |
295 # that's OK, we can just skip it. |
296 |
296 |
297 |
297 |
298 if matchlen > 0: |
298 if matchlen > 0: |
299 assert ia == amatch |
299 assert ia == amatch |
300 assert ib == bmatch |
300 assert ib == bmatch |
301 assert iz == zmatch |
301 assert iz == zmatch |
302 |
302 |
303 yield 'unchanged', zmatch, zend |
303 yield 'unchanged', zmatch, zend |
304 iz = zend |
304 iz = zend |
305 ia = aend |
305 ia = aend |
306 ib = bend |
306 ib = bend |
307 |
307 |
308 |
308 |
309 def reprocess_merge_regions(self, merge_regions): |
309 def reprocess_merge_regions(self, merge_regions): |
310 """Where there are conflict regions, remove the agreed lines. |
310 """Where there are conflict regions, remove the agreed lines. |
311 |
311 |
312 Lines where both A and B have made the same changes are |
312 Lines where both A and B have made the same changes are |
313 eliminated. |
313 eliminated. |
314 """ |
314 """ |
315 for region in merge_regions: |
315 for region in merge_regions: |
316 if region[0] != "conflict": |
316 if region[0] != "conflict": |
317 yield region |
317 yield region |
340 |
340 |
341 def mismatch_region(next_a, region_ia, next_b, region_ib): |
341 def mismatch_region(next_a, region_ia, next_b, region_ib): |
342 if next_a < region_ia or next_b < region_ib: |
342 if next_a < region_ia or next_b < region_ib: |
343 return 'conflict', None, None, next_a, region_ia, next_b, region_ib |
343 return 'conflict', None, None, next_a, region_ia, next_b, region_ib |
344 mismatch_region = staticmethod(mismatch_region) |
344 mismatch_region = staticmethod(mismatch_region) |
345 |
345 |
346 |
346 |
347 def find_sync_regions(self): |
347 def find_sync_regions(self): |
348 """Return a list of sync regions, where both descendents match the base. |
348 """Return a list of sync regions, where both descendents match the base. |
349 |
349 |
350 Generates a list of (base1, base2, a1, a2, b1, b2). There is |
350 Generates a list of (base1, base2, a1, a2, b1, b2). There is |
395 # advance whichever one ends first in the base text |
395 # advance whichever one ends first in the base text |
396 if (abase + alen) < (bbase + blen): |
396 if (abase + alen) < (bbase + blen): |
397 ia += 1 |
397 ia += 1 |
398 else: |
398 else: |
399 ib += 1 |
399 ib += 1 |
400 |
400 |
401 intbase = len(self.base) |
401 intbase = len(self.base) |
402 abase = len(self.a) |
402 abase = len(self.a) |
403 bbase = len(self.b) |
403 bbase = len(self.b) |
404 sl.append((intbase, intbase, abase, abase, bbase, bbase)) |
404 sl.append((intbase, intbase, abase, abase, bbase, bbase)) |
405 |
405 |
509 ('q', 'quiet', None, _('suppress output'))] |
509 ('q', 'quiet', None, _('suppress output'))] |
510 |
510 |
511 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER |
511 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER |
512 |
512 |
513 Simple three-way file merge utility with a minimal feature set. |
513 Simple three-way file merge utility with a minimal feature set. |
514 |
514 |
515 Apply to LOCAL the changes necessary to go from BASE to OTHER. |
515 Apply to LOCAL the changes necessary to go from BASE to OTHER. |
516 |
516 |
517 By default, LOCAL is overwritten with the results of this operation. |
517 By default, LOCAL is overwritten with the results of this operation. |
518 ''') |
518 ''') |
519 |
519 |
520 def showhelp(): |
520 def showhelp(): |
521 sys.stdout.write(usage) |
521 sys.stdout.write(usage) |