contrib/simplemerge
changeset 5115 ea7b982b6c08
parent 4406 1ef4445c6506
equal deleted inserted replaced
5097:73fdc8bd3ed8 5115:ea7b982b6c08
    26 from mercurial.i18n import _
    26 from mercurial.i18n import _
    27 
    27 
    28 
    28 
    29 class CantReprocessAndShowBase(Exception):
    29 class CantReprocessAndShowBase(Exception):
    30     pass
    30     pass
    31     
    31 
    32 
    32 
    33 def warn(message):
    33 def warn(message):
    34     sys.stdout.flush()
    34     sys.stdout.flush()
    35     sys.stderr.write(message)
    35     sys.stderr.write(message)
    36     sys.stderr.flush()
    36     sys.stderr.flush()
    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:
    67     for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
    67     for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
    68         if a[ia] != b[ib]:
    68         if a[ia] != b[ib]:
    69             return False
    69             return False
    70     else:
    70     else:
    71         return True
    71         return True
    72         
    72 
    73 
    73 
    74 
    74 
    75 
    75 
    76 class Merge3Text(object):
    76 class Merge3Text(object):
    77     """3-way merge of texts.
    77     """3-way merge of texts.
   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 
   427 
   427 
   428             if a2 < b2:
   428             if a2 < b2:
   429                 del am[0]
   429                 del am[0]
   430             else:
   430             else:
   431                 del bm[0]
   431                 del bm[0]
   432                 
   432 
   433         return unc
   433         return unc
   434 
   434 
   435 
   435 
   436 # bzr compatible interface, for the tests
   436 # bzr compatible interface, for the tests
   437 class Merge3(Merge3Text):
   437 class Merge3(Merge3Text):
   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)