2 # Copyright (C) 2004, 2005 Canonical Ltd
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 # mbp: "you know that thing where cvs gives you conflict markers?"
25 class CantReprocessAndShowBase(Exception):
28 def intersect(ra, rb):
29 """Given two ranges return the range where they intersect or None.
31 >>> intersect((0, 10), (0, 6))
33 >>> intersect((0, 10), (5, 15))
35 >>> intersect((0, 10), (10, 15))
36 >>> intersect((0, 9), (10, 15))
37 >>> intersect((0, 9), (7, 15))
43 sa = max(ra[0], rb[0])
44 sb = min(ra[1], rb[1])
50 def compare_range(a, astart, aend, b, bstart, bend):
51 """Compare a[astart:aend] == b[bstart:bend], without slicing.
53 if (aend-astart) != (bend-bstart):
55 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
61 class Merge3Text(object):
62 """3-way merge of texts.
64 Given strings BASE, OTHER, THIS, tries to produce a combined text
65 incorporating the changes from both BASE->OTHER and BASE->THIS."""
66 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
67 self.basetext = basetext
71 base = mdiff.splitnewlines(basetext)
73 a = mdiff.splitnewlines(atext)
75 b = mdiff.splitnewlines(btext)
84 start_marker='<<<<<<<',
89 """Return merge in cvs-like form.
91 self.conflicts = False
94 if self.a[0].endswith('\r\n'):
96 elif self.a[0].endswith('\r'):
98 if base_marker and reprocess:
99 raise CantReprocessAndShowBase()
101 start_marker = start_marker + ' ' + name_a
103 end_marker = end_marker + ' ' + name_b
104 if name_base and base_marker:
105 base_marker = base_marker + ' ' + name_base
106 merge_regions = self.merge_regions()
107 if reprocess is True:
108 merge_regions = self.reprocess_merge_regions(merge_regions)
109 for t in merge_regions:
111 if what == 'unchanged':
112 for i in range(t[1], t[2]):
114 elif what == 'a' or what == 'same':
115 for i in range(t[1], t[2]):
118 for i in range(t[1], t[2]):
120 elif what == 'conflict':
121 self.conflicts = True
122 yield start_marker + newline
123 for i in range(t[3], t[4]):
125 if base_marker is not None:
126 yield base_marker + newline
127 for i in range(t[1], t[2]):
129 yield mid_marker + newline
130 for i in range(t[5], t[6]):
132 yield end_marker + newline
134 raise ValueError(what)
136 def merge_annotated(self):
137 """Return merge with conflicts, showing origin of lines.
139 Most useful for debugging merge.
141 for t in self.merge_regions():
143 if what == 'unchanged':
144 for i in range(t[1], t[2]):
145 yield 'u | ' + self.base[i]
146 elif what == 'a' or what == 'same':
147 for i in range(t[1], t[2]):
148 yield what[0] + ' | ' + self.a[i]
150 for i in range(t[1], t[2]):
151 yield 'b | ' + self.b[i]
152 elif what == 'conflict':
154 for i in range(t[3], t[4]):
155 yield 'A | ' + self.a[i]
157 for i in range(t[5], t[6]):
158 yield 'B | ' + self.b[i]
161 raise ValueError(what)
163 def merge_groups(self):
164 """Yield sequence of line groups. Each one is a tuple:
167 Lines unchanged from base
173 Lines taken from a (and equal to b)
178 'conflict', base_lines, a_lines, b_lines
179 Lines from base were changed to either a or b and conflict.
181 for t in self.merge_regions():
183 if what == 'unchanged':
184 yield what, self.base[t[1]:t[2]]
185 elif what == 'a' or what == 'same':
186 yield what, self.a[t[1]:t[2]]
188 yield what, self.b[t[1]:t[2]]
189 elif what == 'conflict':
191 self.base[t[1]:t[2]],
195 raise ValueError(what)
197 def merge_regions(self):
198 """Return sequences of matching and conflicting regions.
200 This returns tuples, where the first value says what kind we
203 'unchanged', start, end
204 Take a region of base[start:end]
207 b and a are different from base but give the same result
210 Non-clashing insertion from a[start:end]
212 Method is as follows:
214 The two sequences align only on regions which match the base
215 and both descendents. These are found by doing a two-way diff
216 of each one against the base, and then finding the
217 intersections between those regions. These "sync regions"
218 are by definition unchanged in both and easily dealt with.
220 The regions in between can be in any of three cases:
221 conflicted, or changed on only one side.
224 # section a[0:ia] has been disposed of, etc
227 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
228 #print 'match base [%d:%d]' % (zmatch, zend)
230 matchlen = zend - zmatch
232 assert matchlen == (aend - amatch)
233 assert matchlen == (bend - bmatch)
237 len_base = zmatch - iz
242 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
245 # try to avoid actually slicing the lists
246 equal_a = compare_range(self.a, ia, amatch,
247 self.base, iz, zmatch)
248 equal_b = compare_range(self.b, ib, bmatch,
249 self.base, iz, zmatch)
250 same = compare_range(self.a, ia, amatch,
254 yield 'same', ia, amatch
255 elif equal_a and not equal_b:
256 yield 'b', ib, bmatch
257 elif equal_b and not equal_a:
258 yield 'a', ia, amatch
259 elif not equal_a and not equal_b:
260 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
262 raise AssertionError("can't handle a=b=base but unmatched")
268 # if the same part of the base was deleted on both sides
269 # that's OK, we can just skip it.
277 yield 'unchanged', zmatch, zend
282 def reprocess_merge_regions(self, merge_regions):
283 """Where there are conflict regions, remove the agreed lines.
285 Lines where both A and B have made the same changes are
288 for region in merge_regions:
289 if region[0] != "conflict":
292 type, iz, zmatch, ia, amatch, ib, bmatch = region
293 a_region = self.a[ia:amatch]
294 b_region = self.b[ib:bmatch]
295 matches = mdiff.get_matching_blocks(''.join(a_region),
299 for region_ia, region_ib, region_len in matches[:-1]:
302 reg = self.mismatch_region(next_a, region_ia, next_b,
306 yield 'same', region_ia, region_len+region_ia
307 next_a = region_ia + region_len
308 next_b = region_ib + region_len
309 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
313 def mismatch_region(next_a, region_ia, next_b, region_ib):
314 if next_a < region_ia or next_b < region_ib:
315 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
316 mismatch_region = staticmethod(mismatch_region)
318 def find_sync_regions(self):
319 """Return a list of sync regions, where both descendents match the base.
321 Generates a list of (base1, base2, a1, a2, b1, b2). There is
322 always a zero-length sync region at the end of all the files.
326 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
327 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
328 len_a = len(amatches)
329 len_b = len(bmatches)
333 while ia < len_a and ib < len_b:
334 abase, amatch, alen = amatches[ia]
335 bbase, bmatch, blen = bmatches[ib]
337 # there is an unconflicted block at i; how long does it
338 # extend? until whichever one ends earlier.
339 i = intersect((abase, abase+alen), (bbase, bbase+blen))
343 intlen = intend - intbase
345 # found a match of base[i[0], i[1]]; this may be less than
346 # the region that matches in either one
347 assert intlen <= alen
348 assert intlen <= blen
349 assert abase <= intbase
350 assert bbase <= intbase
352 asub = amatch + (intbase - abase)
353 bsub = bmatch + (intbase - bbase)
357 assert self.base[intbase:intend] == self.a[asub:aend], \
358 (self.base[intbase:intend], self.a[asub:aend])
360 assert self.base[intbase:intend] == self.b[bsub:bend]
362 sl.append((intbase, intend,
366 # advance whichever one ends first in the base text
367 if (abase + alen) < (bbase + blen):
372 intbase = len(self.base)
375 sl.append((intbase, intbase, abase, abase, bbase, bbase))
379 def find_unconflicted(self):
380 """Return a list of ranges in base that are not conflicted."""
381 am = mdiff.get_matching_blocks(self.basetext, self.atext)
382 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
387 # there is an unconflicted block at i; how long does it
388 # extend? until whichever one ends earlier.
393 i = intersect((a1, a2), (b1, b2))
404 def simplemerge(ui, local, base, other, **opts):
405 def readfile(filename):
406 f = open(filename, "rb")
409 if util.binary(text):
410 msg = _("%s looks like a binary file.") % filename
411 if not opts.get('text'):
412 raise util.Abort(msg)
413 elif not opts.get('quiet'):
414 ui.warn(_('warning: %s\n') % msg)
419 labels = opts.get('label', [])
421 name_a = labels.pop(0)
423 name_b = labels.pop(0)
425 raise util.Abort(_("can only specify two labels."))
427 localtext = readfile(local)
428 basetext = readfile(base)
429 othertext = readfile(other)
431 local = os.path.realpath(local)
432 if not opts.get('print'):
433 opener = util.opener(os.path.dirname(local))
434 out = opener(os.path.basename(local), "w", atomictemp=True)
438 reprocess = not opts.get('no_minimal')
440 m3 = Merge3Text(basetext, localtext, othertext)
441 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
442 reprocess=reprocess):
445 if not opts.get('print'):
449 if not opts.get('quiet'):
450 ui.warn(_("warning: conflicts during merge.\n"))