[Pkg-bazaar-commits] ./bzr/unstable r822: - Renamed merge3 test suite for easier access.

Martin Pool mbp at sourcefrog.net
Fri Apr 10 08:21:08 UTC 2009


------------------------------------------------------------
revno: 822
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Tue 2005-07-05 17:19:12 +1000
message:
  - Renamed merge3 test suite for easier access.
  
  - New merge approach based on finding triple-matching regions, and comparing
    the regions between them; add find_sync_regions() and some tests for it.
renamed:
  bzrlib/selftest/merge3.py => bzrlib/selftest/testmerge3.py
modified:
  bzrlib/merge3.py
  bzrlib/selftest/__init__.py
  bzrlib/selftest/testmerge3.py
-------------- next part --------------
=== modified file 'bzrlib/merge3.py'
--- a/bzrlib/merge3.py	2005-07-04 13:08:55 +0000
+++ b/bzrlib/merge3.py	2005-07-05 07:19:12 +0000
@@ -39,6 +39,16 @@
         return None
 
 
+def threeway(baseline, aline, bline):
+    if baseline == aline:
+        return bline
+    elif baseline == bline:
+        return aline
+    else:
+        return [aline, bline]
+
+
+
 class Merge3(object):
     """3-way merge of texts.
 
@@ -49,21 +59,75 @@
         self.base = base
         self.a = a
         self.b = b
-
-        #from difflib import SequenceMatcher
-
-        #self.a_ops = SequenceMatcher(None, self.base, self.a).get_opcodes()
-        #self.b_ops = SequenceMatcher(None, self.base, self.b).get_opcodes()
+        from difflib import SequenceMatcher
+        self.a_ops = SequenceMatcher(None, base, a).get_opcodes()
+        self.b_ops = SequenceMatcher(None, base, b).get_opcodes()
+
+
+    def merge(self):
+        """Return sequences of matching and conflicting regions.
+
+        Method is as follows:
+
+        The two sequences align only on regions which match the base
+        and both descendents.  These are found by doing a two-way diff
+        of each one against the base, and then finding the
+        intersections between those regions.  These "sync regions"
+        are by definition unchanged in both and easily dealt with.
+
+        The regions in between can be in any of three cases:
+        conflicted, or changed on only one side.
+        """
 
         
-    def find_conflicts(self):
-        """Return a list of conflict regions.
-
-        Each entry is given as (base1, base2, a1, a2, b1, b2).
-
-        This indicates that the range [base1,base2] can be replaced by either
-        [a1,a2] or [b1,b2].
+    def find_sync_regions(self):
+        """Return a list of sync regions, where both descendents match the base.
+
+        Generates a list of ((base1, base2), (a1, a2), (b1, b2)). 
         """
+        from difflib import SequenceMatcher
+        aiter = iter(SequenceMatcher(None, self.base, self.a).get_matching_blocks())
+        biter = iter(SequenceMatcher(None, self.base, self.b).get_matching_blocks())
+
+        abase, amatch, alen = aiter.next()
+        bbase, bmatch, blen = biter.next()
+
+        while aiter and biter:
+            # there is an unconflicted block at i; how long does it
+            # extend?  until whichever one ends earlier.
+            i = intersect((abase, abase+alen), (bbase, bbase+blen))
+            if i:
+                intbase = i[0]
+                intend = i[1]
+                intlen = intend - intbase
+
+                # found a match of base[i[0], i[1]]; this may be less than
+                # the region that matches in either one
+                assert intlen <= alen
+                assert intlen <= blen
+                assert abase <= intbase
+                assert bbase <= intbase
+
+                asub = amatch + (intbase - abase)
+                bsub = bmatch + (intbase - bbase)
+                aend = asub + intlen
+                bend = bsub + intlen
+
+                assert self.base[intbase:intend] == self.a[asub:aend], \
+                       (self.base[intbase:intend], self.a[asub:aend])
+                
+                assert self.base[intbase:intend] == self.b[bsub:bend]
+
+                yield ((intbase, intend),
+                       (asub, aend),
+                       (bsub, bend))
+
+            # advance whichever one ends first in the base text
+            if (abase + alen) < (bbase + blen):
+                abase, amatch, alen = aiter.next()
+            else:
+                bbase, bmatch, blen = biter.next()
+
 
 
     def find_unconflicted(self):

=== modified file 'bzrlib/selftest/__init__.py'
--- a/bzrlib/selftest/__init__.py	2005-07-04 13:08:55 +0000
+++ b/bzrlib/selftest/__init__.py	2005-07-05 07:19:12 +0000
@@ -214,7 +214,7 @@
     import bzrlib.selftest.whitebox
     import bzrlib.selftest.blackbox
     import bzrlib.selftest.versioning
-    import bzrlib.selftest.merge3
+    import bzrlib.selftest.testmerge3
     import bzrlib.merge_core
     from doctest import DocTestSuite
     import os
@@ -237,7 +237,7 @@
 
     for m in bzrlib.selftest.whitebox, \
             bzrlib.selftest.versioning, \
-            bzrlib.selftest.merge3:
+            bzrlib.selftest.testmerge3:
         suite.addTest(tl.loadTestsFromModule(m))
 
     for m in bzrlib.store, bzrlib.inventory, bzrlib.branch, bzrlib.osutils, \

=== renamed file 'bzrlib/selftest/merge3.py' => 'bzrlib/selftest/testmerge3.py'
--- a/bzrlib/selftest/merge3.py	2005-07-04 13:08:55 +0000
+++ b/bzrlib/selftest/testmerge3.py	2005-07-05 07:19:12 +0000
@@ -18,17 +18,6 @@
 from bzrlib.selftest import InTempDir, TestBase
 from bzrlib.merge3 import Merge3
 
-class NoConflicts(TestBase):
-    """No conflicts because only one side changed"""
-    def runTest(self):
-        m3 = Merge3(['aaa', 'bbb'],
-                    ['aaa', '111', 'bbb'],
-                    ['aaa', 'bbb'])
-
-        self.assertEquals(m3.find_unconflicted(),
-                          [(0, 1), (1, 2)])
-
-    
 class NoChanges(TestBase):
     """No conflicts because nothing changed"""
     def runTest(self):
@@ -39,8 +28,27 @@
         self.assertEquals(m3.find_unconflicted(),
                           [(0, 2)])
 
+        self.assertEquals(list(m3.find_sync_regions()),
+                          [((0, 2), (0, 2), (0, 2))])
+
     
 
+class NoConflicts(TestBase):
+    """No conflicts because only one side changed"""
+    def runTest(self):
+        m3 = Merge3(['aaa', 'bbb'],
+                    ['aaa', '111', 'bbb'],
+                    ['aaa', 'bbb'])
+
+        self.assertEquals(m3.find_unconflicted(),
+                          [(0, 1), (1, 2)])
+
+
+        self.assertEquals(list(m3.find_sync_regions()),
+                          [((0, 1), (0, 1), (0, 1)),
+                           ((1, 2), (2, 3), (1, 2))])
+
+
 class InsertClash(TestBase):
     """Both try to insert lines in the same place."""
     def runTest(self):
@@ -51,7 +59,9 @@
         self.assertEquals(m3.find_unconflicted(),
                           [(0, 1), (1, 2)])
 
-        
+        self.assertEquals(list(m3.find_sync_regions()),
+                          [((0, 1), (0, 1), (0, 1)),
+                           ((1, 2), (2, 3), (2, 3))])
 
 
 
@@ -65,4 +75,26 @@
         self.assertEquals(m3.find_unconflicted(),
                           [(0, 1), (2, 3)])
 
+        self.assertEquals(list(m3.find_sync_regions()),
+                          [((0, 1), (0, 1), (0, 1)),
+                           ((2, 3), (2, 3), (2, 3))])
+
+
+
+class ReplaceMulti(TestBase):
+    """Replacement with regions of different size."""
+    def runTest(self):
+        m3 = Merge3(['aaa', '000', '000', 'bbb'],
+                    ['aaa', '111', '111', '111', 'bbb'],
+                    ['aaa', '222', '222', '222', '222', 'bbb'])
+
+        self.assertEquals(m3.find_unconflicted(),
+                          [(0, 1), (3, 4)])
+
+
+        self.assertEquals(list(m3.find_sync_regions()),
+                          [((0, 1), (0, 1), (0, 1)),
+                           ((3, 4), (4, 5), (5, 6))])
+
+        
         



More information about the Pkg-bazaar-commits mailing list