mercurial/simplemerge.py
changeset 52077 a021da4ec509
parent 51859 f4733654f144
equal deleted inserted replaced
52076:513b413702e8 52077:a021da4ec509
    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):
   496     local,
   563     local,
   497     base,
   564     base,
   498     other,
   565     other,
   499     mode=b'merge',
   566     mode=b'merge',
   500     allow_binary=False,
   567     allow_binary=False,
       
   568     relaxed_sync=False,
   501 ):
   569 ):
   502     """Performs the simplemerge algorithm.
   570     """Performs the simplemerge algorithm.
   503 
   571 
   504     The merged result is written into `localctx`.
   572     The merged result is written into `localctx`.
   505     """
   573     """
   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))