Mercurial > hg > mercurial-crew-with-dirclash
comparison contrib/simplemerge @ 4359:2e3c54fb79a3
actually port simplemerge to hg
- use bdiff instead of patiencediff; this is a larger change, since
bdiff works on 2 multi-line strings, while patiencediff works on 2
lists;
- rename the main class from Merge3 to Merge3Text and add a Merge3
class that derives from Merge3Text. This new Merge3 class has
the same interface from the original class, so that the tests
still work;
- Merge3 uses util.binary to detect binary data and raises
util.Abort instead of a specific exception;
- don't use the @decorator syntax, to keep python2.3 compatibility;
- the test uses unittest, which likes to print how long it took to
run. This obviously doesn't play too well with hg's test suite,
so we override time.time to fool unittest;
- one test has a different (but still valid) output because of the
different diff algorithm used;
- the TestCase class used by bzr has some extras to help debugging.
test-merge3.py used 2 of them:
- log method to log some data
- assertEqualDiff method to ease viewing diffs of diffs
We add a dummy log method and use regular assertEquals instead of
assertEqualDiff.
- make simplemerge executable and add "#!/usr/bin/env python" header
author | Alexis S. L. Carvalho <alexis@cecm.usp.br> |
---|---|
date | Mon, 16 Apr 2007 20:17:39 -0300 |
parents | 465b9ea02868 |
children | d5c3a70f8422 |
comparison
equal
deleted
inserted
replaced
4358:465b9ea02868 | 4359:2e3c54fb79a3 |
---|---|
1 #!/usr/bin/env python | |
1 # Copyright (C) 2004, 2005 Canonical Ltd | 2 # Copyright (C) 2004, 2005 Canonical Ltd |
2 # | 3 # |
3 # This program is free software; you can redistribute it and/or modify | 4 # This program is free software; you can redistribute it and/or modify |
4 # it under the terms of the GNU General Public License as published by | 5 # it under the terms of the GNU General Public License as published by |
5 # the Free Software Foundation; either version 2 of the License, or | 6 # the Free Software Foundation; either version 2 of the License, or |
17 | 18 |
18 # mbp: "you know that thing where cvs gives you conflict markers?" | 19 # mbp: "you know that thing where cvs gives you conflict markers?" |
19 # s: "i hate that." | 20 # s: "i hate that." |
20 | 21 |
21 | 22 |
22 from bzrlib.errors import CantReprocessAndShowBase | 23 from mercurial import util, mdiff |
23 import bzrlib.patiencediff | 24 from mercurial.i18n import _ |
24 from bzrlib.textfile import check_text_lines | 25 |
26 | |
27 class CantReprocessAndShowBase(Exception): | |
28 pass | |
25 | 29 |
26 | 30 |
27 def intersect(ra, rb): | 31 def intersect(ra, rb): |
28 """Given two ranges return the range where they intersect or None. | 32 """Given two ranges return the range where they intersect or None. |
29 | 33 |
59 return True | 63 return True |
60 | 64 |
61 | 65 |
62 | 66 |
63 | 67 |
64 class Merge3(object): | 68 class Merge3Text(object): |
65 """3-way merge of texts. | 69 """3-way merge of texts. |
66 | 70 |
67 Given BASE, OTHER, THIS, tries to produce a combined text | 71 Given strings BASE, OTHER, THIS, tries to produce a combined text |
68 incorporating the changes from both BASE->OTHER and BASE->THIS. | 72 incorporating the changes from both BASE->OTHER and BASE->THIS.""" |
69 All three will typically be sequences of lines.""" | 73 def __init__(self, basetext, atext, btext, base=None, a=None, b=None): |
70 def __init__(self, base, a, b): | 74 self.basetext = basetext |
71 check_text_lines(base) | 75 self.atext = atext |
72 check_text_lines(a) | 76 self.btext = btext |
73 check_text_lines(b) | 77 if base is None: |
78 base = mdiff.splitnewlines(basetext) | |
79 if a is None: | |
80 a = mdiff.splitnewlines(atext) | |
81 if b is None: | |
82 b = mdiff.splitnewlines(btext) | |
74 self.base = base | 83 self.base = base |
75 self.a = a | 84 self.a = a |
76 self.b = b | 85 self.b = b |
77 | 86 |
78 | 87 |
298 yield region | 307 yield region |
299 continue | 308 continue |
300 type, iz, zmatch, ia, amatch, ib, bmatch = region | 309 type, iz, zmatch, ia, amatch, ib, bmatch = region |
301 a_region = self.a[ia:amatch] | 310 a_region = self.a[ia:amatch] |
302 b_region = self.b[ib:bmatch] | 311 b_region = self.b[ib:bmatch] |
303 matches = bzrlib.patiencediff.PatienceSequenceMatcher( | 312 matches = mdiff.get_matching_blocks(''.join(a_region), |
304 None, a_region, b_region).get_matching_blocks() | 313 ''.join(b_region)) |
305 next_a = ia | 314 next_a = ia |
306 next_b = ib | 315 next_b = ib |
307 for region_ia, region_ib, region_len in matches[:-1]: | 316 for region_ia, region_ib, region_len in matches[:-1]: |
308 region_ia += ia | 317 region_ia += ia |
309 region_ib += ib | 318 region_ib += ib |
317 reg = self.mismatch_region(next_a, amatch, next_b, bmatch) | 326 reg = self.mismatch_region(next_a, amatch, next_b, bmatch) |
318 if reg is not None: | 327 if reg is not None: |
319 yield reg | 328 yield reg |
320 | 329 |
321 | 330 |
322 @staticmethod | |
323 def mismatch_region(next_a, region_ia, next_b, region_ib): | 331 def mismatch_region(next_a, region_ia, next_b, region_ib): |
324 if next_a < region_ia or next_b < region_ib: | 332 if next_a < region_ia or next_b < region_ib: |
325 return 'conflict', None, None, next_a, region_ia, next_b, region_ib | 333 return 'conflict', None, None, next_a, region_ia, next_b, region_ib |
334 mismatch_region = staticmethod(mismatch_region) | |
326 | 335 |
327 | 336 |
328 def find_sync_regions(self): | 337 def find_sync_regions(self): |
329 """Return a list of sync regions, where both descendents match the base. | 338 """Return a list of sync regions, where both descendents match the base. |
330 | 339 |
331 Generates a list of (base1, base2, a1, a2, b1, b2). There is | 340 Generates a list of (base1, base2, a1, a2, b1, b2). There is |
332 always a zero-length sync region at the end of all the files. | 341 always a zero-length sync region at the end of all the files. |
333 """ | 342 """ |
334 | 343 |
335 ia = ib = 0 | 344 ia = ib = 0 |
336 amatches = bzrlib.patiencediff.PatienceSequenceMatcher( | 345 amatches = mdiff.get_matching_blocks(self.basetext, self.atext) |
337 None, self.base, self.a).get_matching_blocks() | 346 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext) |
338 bmatches = bzrlib.patiencediff.PatienceSequenceMatcher( | |
339 None, self.base, self.b).get_matching_blocks() | |
340 len_a = len(amatches) | 347 len_a = len(amatches) |
341 len_b = len(bmatches) | 348 len_b = len(bmatches) |
342 | 349 |
343 sl = [] | 350 sl = [] |
344 | 351 |
390 | 397 |
391 | 398 |
392 | 399 |
393 def find_unconflicted(self): | 400 def find_unconflicted(self): |
394 """Return a list of ranges in base that are not conflicted.""" | 401 """Return a list of ranges in base that are not conflicted.""" |
395 am = bzrlib.patiencediff.PatienceSequenceMatcher( | 402 am = mdiff.get_matching_blocks(self.basetext, self.atext) |
396 None, self.base, self.a).get_matching_blocks() | 403 bm = mdiff.get_matching_blocks(self.basetext, self.btext) |
397 bm = bzrlib.patiencediff.PatienceSequenceMatcher( | |
398 None, self.base, self.b).get_matching_blocks() | |
399 | 404 |
400 unc = [] | 405 unc = [] |
401 | 406 |
402 while am and bm: | 407 while am and bm: |
403 # there is an unconflicted block at i; how long does it | 408 # there is an unconflicted block at i; how long does it |
416 del bm[0] | 421 del bm[0] |
417 | 422 |
418 return unc | 423 return unc |
419 | 424 |
420 | 425 |
426 # bzr compatible interface, for the tests | |
427 class Merge3(Merge3Text): | |
428 """3-way merge of texts. | |
429 | |
430 Given BASE, OTHER, THIS, tries to produce a combined text | |
431 incorporating the changes from both BASE->OTHER and BASE->THIS. | |
432 All three will typically be sequences of lines.""" | |
433 def __init__(self, base, a, b): | |
434 basetext = '\n'.join([i.strip('\n') for i in base] + ['']) | |
435 atext = '\n'.join([i.strip('\n') for i in a] + ['']) | |
436 btext = '\n'.join([i.strip('\n') for i in b] + ['']) | |
437 if util.binary(basetext) or util.binary(atext) or util.binary(btext): | |
438 raise util.Abort(_("don't know how to merge binary files")) | |
439 Merge3Text.__init__(self, basetext, atext, btext, base, a, b) | |
440 | |
441 | |
421 def main(argv): | 442 def main(argv): |
422 # as for diff3 and meld the syntax is "MINE BASE OTHER" | 443 # as for diff3 and meld the syntax is "MINE BASE OTHER" |
423 a = file(argv[1], 'rt').readlines() | 444 a = file(argv[1], 'rt').readlines() |
424 base = file(argv[2], 'rt').readlines() | 445 base = file(argv[2], 'rt').readlines() |
425 b = file(argv[3], 'rt').readlines() | 446 b = file(argv[3], 'rt').readlines() |