##// END OF EJS Templates
merge: prevent simplemerge from mutating label list...
Durham Goode -
r21272:4aeb7a60 default
parent child Browse files
Show More
@@ -1,453 +1,453 b''
1 # Copyright (C) 2004, 2005 Canonical Ltd
1 # Copyright (C) 2004, 2005 Canonical Ltd
2 #
2 #
3 # This program is free software; you can redistribute it and/or modify
3 # 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
4 # 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
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
6 # (at your option) any later version.
7 #
7 #
8 # This program is distributed in the hope that it will be useful,
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
11 # GNU General Public License for more details.
12 #
12 #
13 # You should have received a copy of the GNU General Public License
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
14 # along with this program; if not, see <http://www.gnu.org/licenses/>.
15
15
16 # mbp: "you know that thing where cvs gives you conflict markers?"
16 # mbp: "you know that thing where cvs gives you conflict markers?"
17 # s: "i hate that."
17 # s: "i hate that."
18
18
19 from i18n import _
19 from i18n import _
20 import scmutil, util, mdiff
20 import scmutil, util, mdiff
21 import sys, os
21 import sys, os
22
22
23 class CantReprocessAndShowBase(Exception):
23 class CantReprocessAndShowBase(Exception):
24 pass
24 pass
25
25
26 def intersect(ra, rb):
26 def intersect(ra, rb):
27 """Given two ranges return the range where they intersect or None.
27 """Given two ranges return the range where they intersect or None.
28
28
29 >>> intersect((0, 10), (0, 6))
29 >>> intersect((0, 10), (0, 6))
30 (0, 6)
30 (0, 6)
31 >>> intersect((0, 10), (5, 15))
31 >>> intersect((0, 10), (5, 15))
32 (5, 10)
32 (5, 10)
33 >>> intersect((0, 10), (10, 15))
33 >>> intersect((0, 10), (10, 15))
34 >>> intersect((0, 9), (10, 15))
34 >>> intersect((0, 9), (10, 15))
35 >>> intersect((0, 9), (7, 15))
35 >>> intersect((0, 9), (7, 15))
36 (7, 9)
36 (7, 9)
37 """
37 """
38 assert ra[0] <= ra[1]
38 assert ra[0] <= ra[1]
39 assert rb[0] <= rb[1]
39 assert rb[0] <= rb[1]
40
40
41 sa = max(ra[0], rb[0])
41 sa = max(ra[0], rb[0])
42 sb = min(ra[1], rb[1])
42 sb = min(ra[1], rb[1])
43 if sa < sb:
43 if sa < sb:
44 return sa, sb
44 return sa, sb
45 else:
45 else:
46 return None
46 return None
47
47
48 def compare_range(a, astart, aend, b, bstart, bend):
48 def compare_range(a, astart, aend, b, bstart, bend):
49 """Compare a[astart:aend] == b[bstart:bend], without slicing.
49 """Compare a[astart:aend] == b[bstart:bend], without slicing.
50 """
50 """
51 if (aend - astart) != (bend - bstart):
51 if (aend - astart) != (bend - bstart):
52 return False
52 return False
53 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
53 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
54 if a[ia] != b[ib]:
54 if a[ia] != b[ib]:
55 return False
55 return False
56 else:
56 else:
57 return True
57 return True
58
58
59 class Merge3Text(object):
59 class Merge3Text(object):
60 """3-way merge of texts.
60 """3-way merge of texts.
61
61
62 Given strings BASE, OTHER, THIS, tries to produce a combined text
62 Given strings BASE, OTHER, THIS, tries to produce a combined text
63 incorporating the changes from both BASE->OTHER and BASE->THIS."""
63 incorporating the changes from both BASE->OTHER and BASE->THIS."""
64 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
64 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
65 self.basetext = basetext
65 self.basetext = basetext
66 self.atext = atext
66 self.atext = atext
67 self.btext = btext
67 self.btext = btext
68 if base is None:
68 if base is None:
69 base = mdiff.splitnewlines(basetext)
69 base = mdiff.splitnewlines(basetext)
70 if a is None:
70 if a is None:
71 a = mdiff.splitnewlines(atext)
71 a = mdiff.splitnewlines(atext)
72 if b is None:
72 if b is None:
73 b = mdiff.splitnewlines(btext)
73 b = mdiff.splitnewlines(btext)
74 self.base = base
74 self.base = base
75 self.a = a
75 self.a = a
76 self.b = b
76 self.b = b
77
77
78 def merge_lines(self,
78 def merge_lines(self,
79 name_a=None,
79 name_a=None,
80 name_b=None,
80 name_b=None,
81 name_base=None,
81 name_base=None,
82 start_marker='<<<<<<<',
82 start_marker='<<<<<<<',
83 mid_marker='=======',
83 mid_marker='=======',
84 end_marker='>>>>>>>',
84 end_marker='>>>>>>>',
85 base_marker=None,
85 base_marker=None,
86 reprocess=False):
86 reprocess=False):
87 """Return merge in cvs-like form.
87 """Return merge in cvs-like form.
88 """
88 """
89 self.conflicts = False
89 self.conflicts = False
90 newline = '\n'
90 newline = '\n'
91 if len(self.a) > 0:
91 if len(self.a) > 0:
92 if self.a[0].endswith('\r\n'):
92 if self.a[0].endswith('\r\n'):
93 newline = '\r\n'
93 newline = '\r\n'
94 elif self.a[0].endswith('\r'):
94 elif self.a[0].endswith('\r'):
95 newline = '\r'
95 newline = '\r'
96 if base_marker and reprocess:
96 if base_marker and reprocess:
97 raise CantReprocessAndShowBase
97 raise CantReprocessAndShowBase
98 if name_a:
98 if name_a:
99 start_marker = start_marker + ' ' + name_a
99 start_marker = start_marker + ' ' + name_a
100 if name_b:
100 if name_b:
101 end_marker = end_marker + ' ' + name_b
101 end_marker = end_marker + ' ' + name_b
102 if name_base and base_marker:
102 if name_base and base_marker:
103 base_marker = base_marker + ' ' + name_base
103 base_marker = base_marker + ' ' + name_base
104 merge_regions = self.merge_regions()
104 merge_regions = self.merge_regions()
105 if reprocess is True:
105 if reprocess is True:
106 merge_regions = self.reprocess_merge_regions(merge_regions)
106 merge_regions = self.reprocess_merge_regions(merge_regions)
107 for t in merge_regions:
107 for t in merge_regions:
108 what = t[0]
108 what = t[0]
109 if what == 'unchanged':
109 if what == 'unchanged':
110 for i in range(t[1], t[2]):
110 for i in range(t[1], t[2]):
111 yield self.base[i]
111 yield self.base[i]
112 elif what == 'a' or what == 'same':
112 elif what == 'a' or what == 'same':
113 for i in range(t[1], t[2]):
113 for i in range(t[1], t[2]):
114 yield self.a[i]
114 yield self.a[i]
115 elif what == 'b':
115 elif what == 'b':
116 for i in range(t[1], t[2]):
116 for i in range(t[1], t[2]):
117 yield self.b[i]
117 yield self.b[i]
118 elif what == 'conflict':
118 elif what == 'conflict':
119 self.conflicts = True
119 self.conflicts = True
120 yield start_marker + newline
120 yield start_marker + newline
121 for i in range(t[3], t[4]):
121 for i in range(t[3], t[4]):
122 yield self.a[i]
122 yield self.a[i]
123 if base_marker is not None:
123 if base_marker is not None:
124 yield base_marker + newline
124 yield base_marker + newline
125 for i in range(t[1], t[2]):
125 for i in range(t[1], t[2]):
126 yield self.base[i]
126 yield self.base[i]
127 yield mid_marker + newline
127 yield mid_marker + newline
128 for i in range(t[5], t[6]):
128 for i in range(t[5], t[6]):
129 yield self.b[i]
129 yield self.b[i]
130 yield end_marker + newline
130 yield end_marker + newline
131 else:
131 else:
132 raise ValueError(what)
132 raise ValueError(what)
133
133
134 def merge_annotated(self):
134 def merge_annotated(self):
135 """Return merge with conflicts, showing origin of lines.
135 """Return merge with conflicts, showing origin of lines.
136
136
137 Most useful for debugging merge.
137 Most useful for debugging merge.
138 """
138 """
139 for t in self.merge_regions():
139 for t in self.merge_regions():
140 what = t[0]
140 what = t[0]
141 if what == 'unchanged':
141 if what == 'unchanged':
142 for i in range(t[1], t[2]):
142 for i in range(t[1], t[2]):
143 yield 'u | ' + self.base[i]
143 yield 'u | ' + self.base[i]
144 elif what == 'a' or what == 'same':
144 elif what == 'a' or what == 'same':
145 for i in range(t[1], t[2]):
145 for i in range(t[1], t[2]):
146 yield what[0] + ' | ' + self.a[i]
146 yield what[0] + ' | ' + self.a[i]
147 elif what == 'b':
147 elif what == 'b':
148 for i in range(t[1], t[2]):
148 for i in range(t[1], t[2]):
149 yield 'b | ' + self.b[i]
149 yield 'b | ' + self.b[i]
150 elif what == 'conflict':
150 elif what == 'conflict':
151 yield '<<<<\n'
151 yield '<<<<\n'
152 for i in range(t[3], t[4]):
152 for i in range(t[3], t[4]):
153 yield 'A | ' + self.a[i]
153 yield 'A | ' + self.a[i]
154 yield '----\n'
154 yield '----\n'
155 for i in range(t[5], t[6]):
155 for i in range(t[5], t[6]):
156 yield 'B | ' + self.b[i]
156 yield 'B | ' + self.b[i]
157 yield '>>>>\n'
157 yield '>>>>\n'
158 else:
158 else:
159 raise ValueError(what)
159 raise ValueError(what)
160
160
161 def merge_groups(self):
161 def merge_groups(self):
162 """Yield sequence of line groups. Each one is a tuple:
162 """Yield sequence of line groups. Each one is a tuple:
163
163
164 'unchanged', lines
164 'unchanged', lines
165 Lines unchanged from base
165 Lines unchanged from base
166
166
167 'a', lines
167 'a', lines
168 Lines taken from a
168 Lines taken from a
169
169
170 'same', lines
170 'same', lines
171 Lines taken from a (and equal to b)
171 Lines taken from a (and equal to b)
172
172
173 'b', lines
173 'b', lines
174 Lines taken from b
174 Lines taken from b
175
175
176 'conflict', base_lines, a_lines, b_lines
176 'conflict', base_lines, a_lines, b_lines
177 Lines from base were changed to either a or b and conflict.
177 Lines from base were changed to either a or b and conflict.
178 """
178 """
179 for t in self.merge_regions():
179 for t in self.merge_regions():
180 what = t[0]
180 what = t[0]
181 if what == 'unchanged':
181 if what == 'unchanged':
182 yield what, self.base[t[1]:t[2]]
182 yield what, self.base[t[1]:t[2]]
183 elif what == 'a' or what == 'same':
183 elif what == 'a' or what == 'same':
184 yield what, self.a[t[1]:t[2]]
184 yield what, self.a[t[1]:t[2]]
185 elif what == 'b':
185 elif what == 'b':
186 yield what, self.b[t[1]:t[2]]
186 yield what, self.b[t[1]:t[2]]
187 elif what == 'conflict':
187 elif what == 'conflict':
188 yield (what,
188 yield (what,
189 self.base[t[1]:t[2]],
189 self.base[t[1]:t[2]],
190 self.a[t[3]:t[4]],
190 self.a[t[3]:t[4]],
191 self.b[t[5]:t[6]])
191 self.b[t[5]:t[6]])
192 else:
192 else:
193 raise ValueError(what)
193 raise ValueError(what)
194
194
195 def merge_regions(self):
195 def merge_regions(self):
196 """Return sequences of matching and conflicting regions.
196 """Return sequences of matching and conflicting regions.
197
197
198 This returns tuples, where the first value says what kind we
198 This returns tuples, where the first value says what kind we
199 have:
199 have:
200
200
201 'unchanged', start, end
201 'unchanged', start, end
202 Take a region of base[start:end]
202 Take a region of base[start:end]
203
203
204 'same', astart, aend
204 'same', astart, aend
205 b and a are different from base but give the same result
205 b and a are different from base but give the same result
206
206
207 'a', start, end
207 'a', start, end
208 Non-clashing insertion from a[start:end]
208 Non-clashing insertion from a[start:end]
209
209
210 Method is as follows:
210 Method is as follows:
211
211
212 The two sequences align only on regions which match the base
212 The two sequences align only on regions which match the base
213 and both descendants. These are found by doing a two-way diff
213 and both descendants. These are found by doing a two-way diff
214 of each one against the base, and then finding the
214 of each one against the base, and then finding the
215 intersections between those regions. These "sync regions"
215 intersections between those regions. These "sync regions"
216 are by definition unchanged in both and easily dealt with.
216 are by definition unchanged in both and easily dealt with.
217
217
218 The regions in between can be in any of three cases:
218 The regions in between can be in any of three cases:
219 conflicted, or changed on only one side.
219 conflicted, or changed on only one side.
220 """
220 """
221
221
222 # section a[0:ia] has been disposed of, etc
222 # section a[0:ia] has been disposed of, etc
223 iz = ia = ib = 0
223 iz = ia = ib = 0
224
224
225 for region in self.find_sync_regions():
225 for region in self.find_sync_regions():
226 zmatch, zend, amatch, aend, bmatch, bend = region
226 zmatch, zend, amatch, aend, bmatch, bend = region
227 #print 'match base [%d:%d]' % (zmatch, zend)
227 #print 'match base [%d:%d]' % (zmatch, zend)
228
228
229 matchlen = zend - zmatch
229 matchlen = zend - zmatch
230 assert matchlen >= 0
230 assert matchlen >= 0
231 assert matchlen == (aend - amatch)
231 assert matchlen == (aend - amatch)
232 assert matchlen == (bend - bmatch)
232 assert matchlen == (bend - bmatch)
233
233
234 len_a = amatch - ia
234 len_a = amatch - ia
235 len_b = bmatch - ib
235 len_b = bmatch - ib
236 len_base = zmatch - iz
236 len_base = zmatch - iz
237 assert len_a >= 0
237 assert len_a >= 0
238 assert len_b >= 0
238 assert len_b >= 0
239 assert len_base >= 0
239 assert len_base >= 0
240
240
241 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
241 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
242
242
243 if len_a or len_b:
243 if len_a or len_b:
244 # try to avoid actually slicing the lists
244 # try to avoid actually slicing the lists
245 equal_a = compare_range(self.a, ia, amatch,
245 equal_a = compare_range(self.a, ia, amatch,
246 self.base, iz, zmatch)
246 self.base, iz, zmatch)
247 equal_b = compare_range(self.b, ib, bmatch,
247 equal_b = compare_range(self.b, ib, bmatch,
248 self.base, iz, zmatch)
248 self.base, iz, zmatch)
249 same = compare_range(self.a, ia, amatch,
249 same = compare_range(self.a, ia, amatch,
250 self.b, ib, bmatch)
250 self.b, ib, bmatch)
251
251
252 if same:
252 if same:
253 yield 'same', ia, amatch
253 yield 'same', ia, amatch
254 elif equal_a and not equal_b:
254 elif equal_a and not equal_b:
255 yield 'b', ib, bmatch
255 yield 'b', ib, bmatch
256 elif equal_b and not equal_a:
256 elif equal_b and not equal_a:
257 yield 'a', ia, amatch
257 yield 'a', ia, amatch
258 elif not equal_a and not equal_b:
258 elif not equal_a and not equal_b:
259 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
259 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
260 else:
260 else:
261 raise AssertionError("can't handle a=b=base but unmatched")
261 raise AssertionError("can't handle a=b=base but unmatched")
262
262
263 ia = amatch
263 ia = amatch
264 ib = bmatch
264 ib = bmatch
265 iz = zmatch
265 iz = zmatch
266
266
267 # if the same part of the base was deleted on both sides
267 # if the same part of the base was deleted on both sides
268 # that's OK, we can just skip it.
268 # that's OK, we can just skip it.
269
269
270
270
271 if matchlen > 0:
271 if matchlen > 0:
272 assert ia == amatch
272 assert ia == amatch
273 assert ib == bmatch
273 assert ib == bmatch
274 assert iz == zmatch
274 assert iz == zmatch
275
275
276 yield 'unchanged', zmatch, zend
276 yield 'unchanged', zmatch, zend
277 iz = zend
277 iz = zend
278 ia = aend
278 ia = aend
279 ib = bend
279 ib = bend
280
280
281 def reprocess_merge_regions(self, merge_regions):
281 def reprocess_merge_regions(self, merge_regions):
282 """Where there are conflict regions, remove the agreed lines.
282 """Where there are conflict regions, remove the agreed lines.
283
283
284 Lines where both A and B have made the same changes are
284 Lines where both A and B have made the same changes are
285 eliminated.
285 eliminated.
286 """
286 """
287 for region in merge_regions:
287 for region in merge_regions:
288 if region[0] != "conflict":
288 if region[0] != "conflict":
289 yield region
289 yield region
290 continue
290 continue
291 type, iz, zmatch, ia, amatch, ib, bmatch = region
291 type, iz, zmatch, ia, amatch, ib, bmatch = region
292 a_region = self.a[ia:amatch]
292 a_region = self.a[ia:amatch]
293 b_region = self.b[ib:bmatch]
293 b_region = self.b[ib:bmatch]
294 matches = mdiff.get_matching_blocks(''.join(a_region),
294 matches = mdiff.get_matching_blocks(''.join(a_region),
295 ''.join(b_region))
295 ''.join(b_region))
296 next_a = ia
296 next_a = ia
297 next_b = ib
297 next_b = ib
298 for region_ia, region_ib, region_len in matches[:-1]:
298 for region_ia, region_ib, region_len in matches[:-1]:
299 region_ia += ia
299 region_ia += ia
300 region_ib += ib
300 region_ib += ib
301 reg = self.mismatch_region(next_a, region_ia, next_b,
301 reg = self.mismatch_region(next_a, region_ia, next_b,
302 region_ib)
302 region_ib)
303 if reg is not None:
303 if reg is not None:
304 yield reg
304 yield reg
305 yield 'same', region_ia, region_len + region_ia
305 yield 'same', region_ia, region_len + region_ia
306 next_a = region_ia + region_len
306 next_a = region_ia + region_len
307 next_b = region_ib + region_len
307 next_b = region_ib + region_len
308 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
308 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
309 if reg is not None:
309 if reg is not None:
310 yield reg
310 yield reg
311
311
312 def mismatch_region(next_a, region_ia, next_b, region_ib):
312 def mismatch_region(next_a, region_ia, next_b, region_ib):
313 if next_a < region_ia or next_b < region_ib:
313 if next_a < region_ia or next_b < region_ib:
314 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
314 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
315 mismatch_region = staticmethod(mismatch_region)
315 mismatch_region = staticmethod(mismatch_region)
316
316
317 def find_sync_regions(self):
317 def find_sync_regions(self):
318 """Return a list of sync regions, where both descendants match the base.
318 """Return a list of sync regions, where both descendants match the base.
319
319
320 Generates a list of (base1, base2, a1, a2, b1, b2). There is
320 Generates a list of (base1, base2, a1, a2, b1, b2). There is
321 always a zero-length sync region at the end of all the files.
321 always a zero-length sync region at the end of all the files.
322 """
322 """
323
323
324 ia = ib = 0
324 ia = ib = 0
325 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
325 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
326 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
326 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
327 len_a = len(amatches)
327 len_a = len(amatches)
328 len_b = len(bmatches)
328 len_b = len(bmatches)
329
329
330 sl = []
330 sl = []
331
331
332 while ia < len_a and ib < len_b:
332 while ia < len_a and ib < len_b:
333 abase, amatch, alen = amatches[ia]
333 abase, amatch, alen = amatches[ia]
334 bbase, bmatch, blen = bmatches[ib]
334 bbase, bmatch, blen = bmatches[ib]
335
335
336 # there is an unconflicted block at i; how long does it
336 # there is an unconflicted block at i; how long does it
337 # extend? until whichever one ends earlier.
337 # extend? until whichever one ends earlier.
338 i = intersect((abase, abase + alen), (bbase, bbase + blen))
338 i = intersect((abase, abase + alen), (bbase, bbase + blen))
339 if i:
339 if i:
340 intbase = i[0]
340 intbase = i[0]
341 intend = i[1]
341 intend = i[1]
342 intlen = intend - intbase
342 intlen = intend - intbase
343
343
344 # found a match of base[i[0], i[1]]; this may be less than
344 # found a match of base[i[0], i[1]]; this may be less than
345 # the region that matches in either one
345 # the region that matches in either one
346 assert intlen <= alen
346 assert intlen <= alen
347 assert intlen <= blen
347 assert intlen <= blen
348 assert abase <= intbase
348 assert abase <= intbase
349 assert bbase <= intbase
349 assert bbase <= intbase
350
350
351 asub = amatch + (intbase - abase)
351 asub = amatch + (intbase - abase)
352 bsub = bmatch + (intbase - bbase)
352 bsub = bmatch + (intbase - bbase)
353 aend = asub + intlen
353 aend = asub + intlen
354 bend = bsub + intlen
354 bend = bsub + intlen
355
355
356 assert self.base[intbase:intend] == self.a[asub:aend], \
356 assert self.base[intbase:intend] == self.a[asub:aend], \
357 (self.base[intbase:intend], self.a[asub:aend])
357 (self.base[intbase:intend], self.a[asub:aend])
358
358
359 assert self.base[intbase:intend] == self.b[bsub:bend]
359 assert self.base[intbase:intend] == self.b[bsub:bend]
360
360
361 sl.append((intbase, intend,
361 sl.append((intbase, intend,
362 asub, aend,
362 asub, aend,
363 bsub, bend))
363 bsub, bend))
364
364
365 # advance whichever one ends first in the base text
365 # advance whichever one ends first in the base text
366 if (abase + alen) < (bbase + blen):
366 if (abase + alen) < (bbase + blen):
367 ia += 1
367 ia += 1
368 else:
368 else:
369 ib += 1
369 ib += 1
370
370
371 intbase = len(self.base)
371 intbase = len(self.base)
372 abase = len(self.a)
372 abase = len(self.a)
373 bbase = len(self.b)
373 bbase = len(self.b)
374 sl.append((intbase, intbase, abase, abase, bbase, bbase))
374 sl.append((intbase, intbase, abase, abase, bbase, bbase))
375
375
376 return sl
376 return sl
377
377
378 def find_unconflicted(self):
378 def find_unconflicted(self):
379 """Return a list of ranges in base that are not conflicted."""
379 """Return a list of ranges in base that are not conflicted."""
380 am = mdiff.get_matching_blocks(self.basetext, self.atext)
380 am = mdiff.get_matching_blocks(self.basetext, self.atext)
381 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
381 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
382
382
383 unc = []
383 unc = []
384
384
385 while am and bm:
385 while am and bm:
386 # there is an unconflicted block at i; how long does it
386 # there is an unconflicted block at i; how long does it
387 # extend? until whichever one ends earlier.
387 # extend? until whichever one ends earlier.
388 a1 = am[0][0]
388 a1 = am[0][0]
389 a2 = a1 + am[0][2]
389 a2 = a1 + am[0][2]
390 b1 = bm[0][0]
390 b1 = bm[0][0]
391 b2 = b1 + bm[0][2]
391 b2 = b1 + bm[0][2]
392 i = intersect((a1, a2), (b1, b2))
392 i = intersect((a1, a2), (b1, b2))
393 if i:
393 if i:
394 unc.append(i)
394 unc.append(i)
395
395
396 if a2 < b2:
396 if a2 < b2:
397 del am[0]
397 del am[0]
398 else:
398 else:
399 del bm[0]
399 del bm[0]
400
400
401 return unc
401 return unc
402
402
403 def simplemerge(ui, local, base, other, **opts):
403 def simplemerge(ui, local, base, other, **opts):
404 def readfile(filename):
404 def readfile(filename):
405 f = open(filename, "rb")
405 f = open(filename, "rb")
406 text = f.read()
406 text = f.read()
407 f.close()
407 f.close()
408 if util.binary(text):
408 if util.binary(text):
409 msg = _("%s looks like a binary file.") % filename
409 msg = _("%s looks like a binary file.") % filename
410 if not opts.get('quiet'):
410 if not opts.get('quiet'):
411 ui.warn(_('warning: %s\n') % msg)
411 ui.warn(_('warning: %s\n') % msg)
412 if not opts.get('text'):
412 if not opts.get('text'):
413 raise util.Abort(msg)
413 raise util.Abort(msg)
414 return text
414 return text
415
415
416 name_a = local
416 name_a = local
417 name_b = other
417 name_b = other
418 labels = opts.get('label', [])
418 labels = opts.get('label', [])
419 if labels:
419 if len(labels) > 0:
420 name_a = labels.pop(0)
420 name_a = labels[0]
421 if labels:
421 if len(labels) > 1:
422 name_b = labels.pop(0)
422 name_b = labels[1]
423 if labels:
423 if len(labels) > 2:
424 raise util.Abort(_("can only specify two labels."))
424 raise util.Abort(_("can only specify two labels."))
425
425
426 try:
426 try:
427 localtext = readfile(local)
427 localtext = readfile(local)
428 basetext = readfile(base)
428 basetext = readfile(base)
429 othertext = readfile(other)
429 othertext = readfile(other)
430 except util.Abort:
430 except util.Abort:
431 return 1
431 return 1
432
432
433 local = os.path.realpath(local)
433 local = os.path.realpath(local)
434 if not opts.get('print'):
434 if not opts.get('print'):
435 opener = scmutil.opener(os.path.dirname(local))
435 opener = scmutil.opener(os.path.dirname(local))
436 out = opener(os.path.basename(local), "w", atomictemp=True)
436 out = opener(os.path.basename(local), "w", atomictemp=True)
437 else:
437 else:
438 out = sys.stdout
438 out = sys.stdout
439
439
440 reprocess = not opts.get('no_minimal')
440 reprocess = not opts.get('no_minimal')
441
441
442 m3 = Merge3Text(basetext, localtext, othertext)
442 m3 = Merge3Text(basetext, localtext, othertext)
443 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
443 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
444 reprocess=reprocess):
444 reprocess=reprocess):
445 out.write(line)
445 out.write(line)
446
446
447 if not opts.get('print'):
447 if not opts.get('print'):
448 out.close()
448 out.close()
449
449
450 if m3.conflicts:
450 if m3.conflicts:
451 if not opts.get('quiet'):
451 if not opts.get('quiet'):
452 ui.warn(_("warning: conflicts during merge.\n"))
452 ui.warn(_("warning: conflicts during merge.\n"))
453 return 1
453 return 1
General Comments 0
You need to be logged in to leave comments. Login now