##// END OF EJS Templates
simplemerge: disable a pytype error where it's just confused...
Augie Fackler -
r43787:3b581ad5 default
parent child Browse files
Show More
@@ -1,498 +1,510 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 __future__ import absolute_import
19 from __future__ import absolute_import
20
20
21 from .i18n import _
21 from .i18n import _
22 from . import (
22 from . import (
23 error,
23 error,
24 mdiff,
24 mdiff,
25 pycompat,
25 pycompat,
26 )
26 )
27 from .utils import stringutil
27 from .utils import stringutil
28
28
29
29
30 class CantReprocessAndShowBase(Exception):
30 class CantReprocessAndShowBase(Exception):
31 pass
31 pass
32
32
33
33
34 def intersect(ra, rb):
34 def intersect(ra, rb):
35 """Given two ranges return the range where they intersect or None.
35 """Given two ranges return the range where they intersect or None.
36
36
37 >>> intersect((0, 10), (0, 6))
37 >>> intersect((0, 10), (0, 6))
38 (0, 6)
38 (0, 6)
39 >>> intersect((0, 10), (5, 15))
39 >>> intersect((0, 10), (5, 15))
40 (5, 10)
40 (5, 10)
41 >>> intersect((0, 10), (10, 15))
41 >>> intersect((0, 10), (10, 15))
42 >>> intersect((0, 9), (10, 15))
42 >>> intersect((0, 9), (10, 15))
43 >>> intersect((0, 9), (7, 15))
43 >>> intersect((0, 9), (7, 15))
44 (7, 9)
44 (7, 9)
45 """
45 """
46 assert ra[0] <= ra[1]
46 assert ra[0] <= ra[1]
47 assert rb[0] <= rb[1]
47 assert rb[0] <= rb[1]
48
48
49 sa = max(ra[0], rb[0])
49 sa = max(ra[0], rb[0])
50 sb = min(ra[1], rb[1])
50 sb = min(ra[1], rb[1])
51 if sa < sb:
51 if sa < sb:
52 return sa, sb
52 return sa, sb
53 else:
53 else:
54 return None
54 return None
55
55
56
56
57 def compare_range(a, astart, aend, b, bstart, bend):
57 def compare_range(a, astart, aend, b, bstart, bend):
58 """Compare a[astart:aend] == b[bstart:bend], without slicing.
58 """Compare a[astart:aend] == b[bstart:bend], without slicing.
59 """
59 """
60 if (aend - astart) != (bend - bstart):
60 if (aend - astart) != (bend - bstart):
61 return False
61 return False
62 for ia, ib in zip(
62 for ia, ib in zip(
63 pycompat.xrange(astart, aend), pycompat.xrange(bstart, bend)
63 pycompat.xrange(astart, aend), pycompat.xrange(bstart, bend)
64 ):
64 ):
65 if a[ia] != b[ib]:
65 if a[ia] != b[ib]:
66 return False
66 return False
67 else:
67 else:
68 return True
68 return True
69
69
70
70
71 class Merge3Text(object):
71 class Merge3Text(object):
72 """3-way merge of texts.
72 """3-way merge of texts.
73
73
74 Given strings BASE, OTHER, THIS, tries to produce a combined text
74 Given strings BASE, OTHER, THIS, tries to produce a combined text
75 incorporating the changes from both BASE->OTHER and BASE->THIS."""
75 incorporating the changes from both BASE->OTHER and BASE->THIS."""
76
76
77 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
77 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
78 self.basetext = basetext
78 self.basetext = basetext
79 self.atext = atext
79 self.atext = atext
80 self.btext = btext
80 self.btext = btext
81 if base is None:
81 if base is None:
82 base = mdiff.splitnewlines(basetext)
82 base = mdiff.splitnewlines(basetext)
83 if a is None:
83 if a is None:
84 a = mdiff.splitnewlines(atext)
84 a = mdiff.splitnewlines(atext)
85 if b is None:
85 if b is None:
86 b = mdiff.splitnewlines(btext)
86 b = mdiff.splitnewlines(btext)
87 self.base = base
87 self.base = base
88 self.a = a
88 self.a = a
89 self.b = b
89 self.b = b
90
90
91 def merge_lines(
91 def merge_lines(
92 self,
92 self,
93 name_a=None,
93 name_a=None,
94 name_b=None,
94 name_b=None,
95 name_base=None,
95 name_base=None,
96 start_marker=b'<<<<<<<',
96 start_marker=b'<<<<<<<',
97 mid_marker=b'=======',
97 mid_marker=b'=======',
98 end_marker=b'>>>>>>>',
98 end_marker=b'>>>>>>>',
99 base_marker=None,
99 base_marker=None,
100 localorother=None,
100 localorother=None,
101 minimize=False,
101 minimize=False,
102 ):
102 ):
103 """Return merge in cvs-like form.
103 """Return merge in cvs-like form.
104 """
104 """
105 self.conflicts = False
105 self.conflicts = False
106 newline = b'\n'
106 newline = b'\n'
107 if len(self.a) > 0:
107 if len(self.a) > 0:
108 if self.a[0].endswith(b'\r\n'):
108 if self.a[0].endswith(b'\r\n'):
109 newline = b'\r\n'
109 newline = b'\r\n'
110 elif self.a[0].endswith(b'\r'):
110 elif self.a[0].endswith(b'\r'):
111 newline = b'\r'
111 newline = b'\r'
112 if name_a and start_marker:
112 if name_a and start_marker:
113 start_marker = start_marker + b' ' + name_a
113 start_marker = start_marker + b' ' + name_a
114 if name_b and end_marker:
114 if name_b and end_marker:
115 end_marker = end_marker + b' ' + name_b
115 end_marker = end_marker + b' ' + name_b
116 if name_base and base_marker:
116 if name_base and base_marker:
117 base_marker = base_marker + b' ' + name_base
117 base_marker = base_marker + b' ' + name_base
118 merge_regions = self.merge_regions()
118 merge_regions = self.merge_regions()
119 if minimize:
119 if minimize:
120 merge_regions = self.minimize(merge_regions)
120 merge_regions = self.minimize(merge_regions)
121 for t in merge_regions:
121 for t in merge_regions:
122 what = t[0]
122 what = t[0]
123 if what == b'unchanged':
123 if what == b'unchanged':
124 for i in range(t[1], t[2]):
124 for i in range(t[1], t[2]):
125 yield self.base[i]
125 yield self.base[i]
126 elif what == b'a' or what == b'same':
126 elif what == b'a' or what == b'same':
127 for i in range(t[1], t[2]):
127 for i in range(t[1], t[2]):
128 yield self.a[i]
128 yield self.a[i]
129 elif what == b'b':
129 elif what == b'b':
130 for i in range(t[1], t[2]):
130 for i in range(t[1], t[2]):
131 yield self.b[i]
131 yield self.b[i]
132 elif what == b'conflict':
132 elif what == b'conflict':
133 if localorother == b'local':
133 if localorother == b'local':
134 for i in range(t[3], t[4]):
134 for i in range(t[3], t[4]):
135 yield self.a[i]
135 yield self.a[i]
136 elif localorother == b'other':
136 elif localorother == b'other':
137 for i in range(t[5], t[6]):
137 for i in range(t[5], t[6]):
138 yield self.b[i]
138 yield self.b[i]
139 else:
139 else:
140 self.conflicts = True
140 self.conflicts = True
141 if start_marker is not None:
141 if start_marker is not None:
142 yield start_marker + newline
142 yield start_marker + newline
143 for i in range(t[3], t[4]):
143 for i in range(t[3], t[4]):
144 yield self.a[i]
144 yield self.a[i]
145 if base_marker is not None:
145 if base_marker is not None:
146 yield base_marker + newline
146 yield base_marker + newline
147 for i in range(t[1], t[2]):
147 for i in range(t[1], t[2]):
148 yield self.base[i]
148 yield self.base[i]
149 if mid_marker is not None:
149 if mid_marker is not None:
150 yield mid_marker + newline
150 yield mid_marker + newline
151 for i in range(t[5], t[6]):
151 for i in range(t[5], t[6]):
152 yield self.b[i]
152 yield self.b[i]
153 if end_marker is not None:
153 if end_marker is not None:
154 yield end_marker + newline
154 yield end_marker + newline
155 else:
155 else:
156 raise ValueError(what)
156 raise ValueError(what)
157
157
158 def merge_groups(self):
158 def merge_groups(self):
159 """Yield sequence of line groups. Each one is a tuple:
159 """Yield sequence of line groups. Each one is a tuple:
160
160
161 'unchanged', lines
161 'unchanged', lines
162 Lines unchanged from base
162 Lines unchanged from base
163
163
164 'a', lines
164 'a', lines
165 Lines taken from a
165 Lines taken from a
166
166
167 'same', lines
167 'same', lines
168 Lines taken from a (and equal to b)
168 Lines taken from a (and equal to b)
169
169
170 'b', lines
170 'b', lines
171 Lines taken from b
171 Lines taken from b
172
172
173 'conflict', base_lines, a_lines, b_lines
173 'conflict', base_lines, a_lines, b_lines
174 Lines from base were changed to either a or b and conflict.
174 Lines from base were changed to either a or b and conflict.
175 """
175 """
176 for t in self.merge_regions():
176 for t in self.merge_regions():
177 what = t[0]
177 what = t[0]
178 if what == b'unchanged':
178 if what == b'unchanged':
179 yield what, self.base[t[1] : t[2]]
179 yield what, self.base[t[1] : t[2]]
180 elif what == b'a' or what == b'same':
180 elif what == b'a' or what == b'same':
181 yield what, self.a[t[1] : t[2]]
181 yield what, self.a[t[1] : t[2]]
182 elif what == b'b':
182 elif what == b'b':
183 yield what, self.b[t[1] : t[2]]
183 yield what, self.b[t[1] : t[2]]
184 elif what == b'conflict':
184 elif what == b'conflict':
185 yield (
185 yield (
186 what,
186 what,
187 self.base[t[1] : t[2]],
187 self.base[t[1] : t[2]],
188 self.a[t[3] : t[4]],
188 self.a[t[3] : t[4]],
189 self.b[t[5] : t[6]],
189 self.b[t[5] : t[6]],
190 )
190 )
191 else:
191 else:
192 raise ValueError(what)
192 raise ValueError(what)
193
193
194 def merge_regions(self):
194 def merge_regions(self):
195 """Return sequences of matching and conflicting regions.
195 """Return sequences of matching and conflicting regions.
196
196
197 This returns tuples, where the first value says what kind we
197 This returns tuples, where the first value says what kind we
198 have:
198 have:
199
199
200 'unchanged', start, end
200 'unchanged', start, end
201 Take a region of base[start:end]
201 Take a region of base[start:end]
202
202
203 'same', astart, aend
203 'same', astart, aend
204 b and a are different from base but give the same result
204 b and a are different from base but give the same result
205
205
206 'a', start, end
206 'a', start, end
207 Non-clashing insertion from a[start:end]
207 Non-clashing insertion from a[start:end]
208
208
209 'conflict', zstart, zend, astart, aend, bstart, bend
209 'conflict', zstart, zend, astart, aend, bstart, bend
210 Conflict between a and b, with z as common ancestor
210 Conflict between a and b, with z as common ancestor
211
211
212 Method is as follows:
212 Method is as follows:
213
213
214 The two sequences align only on regions which match the base
214 The two sequences align only on regions which match the base
215 and both descendants. These are found by doing a two-way diff
215 and both descendants. These are found by doing a two-way diff
216 of each one against the base, and then finding the
216 of each one against the base, and then finding the
217 intersections between those regions. These "sync regions"
217 intersections between those regions. These "sync regions"
218 are by definition unchanged in both and easily dealt with.
218 are by definition unchanged in both and easily dealt with.
219
219
220 The regions in between can be in any of three cases:
220 The regions in between can be in any of three cases:
221 conflicted, or changed on only one side.
221 conflicted, or changed on only one side.
222 """
222 """
223
223
224 # section a[0:ia] has been disposed of, etc
224 # section a[0:ia] has been disposed of, etc
225 iz = ia = ib = 0
225 iz = ia = ib = 0
226
226
227 for region in self.find_sync_regions():
227 for region in self.find_sync_regions():
228 zmatch, zend, amatch, aend, bmatch, bend = region
228 zmatch, zend, amatch, aend, bmatch, bend = region
229 # print 'match base [%d:%d]' % (zmatch, zend)
229 # print 'match base [%d:%d]' % (zmatch, zend)
230
230
231 matchlen = zend - zmatch
231 matchlen = zend - zmatch
232 assert matchlen >= 0
232 assert matchlen >= 0
233 assert matchlen == (aend - amatch)
233 assert matchlen == (aend - amatch)
234 assert matchlen == (bend - bmatch)
234 assert matchlen == (bend - bmatch)
235
235
236 len_a = amatch - ia
236 len_a = amatch - ia
237 len_b = bmatch - ib
237 len_b = bmatch - ib
238 len_base = zmatch - iz
238 len_base = zmatch - iz
239 assert len_a >= 0
239 assert len_a >= 0
240 assert len_b >= 0
240 assert len_b >= 0
241 assert len_base >= 0
241 assert len_base >= 0
242
242
243 # print 'unmatched a=%d, b=%d' % (len_a, len_b)
243 # print 'unmatched a=%d, b=%d' % (len_a, len_b)
244
244
245 if len_a or len_b:
245 if len_a or len_b:
246 # try to avoid actually slicing the lists
246 # try to avoid actually slicing the lists
247 equal_a = compare_range(
247 equal_a = compare_range(
248 self.a, ia, amatch, self.base, iz, zmatch
248 self.a, ia, amatch, self.base, iz, zmatch
249 )
249 )
250 equal_b = compare_range(
250 equal_b = compare_range(
251 self.b, ib, bmatch, self.base, iz, zmatch
251 self.b, ib, bmatch, self.base, iz, zmatch
252 )
252 )
253 same = compare_range(self.a, ia, amatch, self.b, ib, bmatch)
253 same = compare_range(self.a, ia, amatch, self.b, ib, bmatch)
254
254
255 if same:
255 if same:
256 yield b'same', ia, amatch
256 yield b'same', ia, amatch
257 elif equal_a and not equal_b:
257 elif equal_a and not equal_b:
258 yield b'b', ib, bmatch
258 yield b'b', ib, bmatch
259 elif equal_b and not equal_a:
259 elif equal_b and not equal_a:
260 yield b'a', ia, amatch
260 yield b'a', ia, amatch
261 elif not equal_a and not equal_b:
261 elif not equal_a and not equal_b:
262 yield b'conflict', iz, zmatch, ia, amatch, ib, bmatch
262 yield b'conflict', iz, zmatch, ia, amatch, ib, bmatch
263 else:
263 else:
264 raise AssertionError(b"can't handle a=b=base but unmatched")
264 raise AssertionError(b"can't handle a=b=base but unmatched")
265
265
266 ia = amatch
266 ia = amatch
267 ib = bmatch
267 ib = bmatch
268 iz = zmatch
268 iz = zmatch
269
269
270 # if the same part of the base was deleted on both sides
270 # if the same part of the base was deleted on both sides
271 # that's OK, we can just skip it.
271 # that's OK, we can just skip it.
272
272
273 if matchlen > 0:
273 if matchlen > 0:
274 assert ia == amatch
274 assert ia == amatch
275 assert ib == bmatch
275 assert ib == bmatch
276 assert iz == zmatch
276 assert iz == zmatch
277
277
278 yield b'unchanged', zmatch, zend
278 yield b'unchanged', zmatch, zend
279 iz = zend
279 iz = zend
280 ia = aend
280 ia = aend
281 ib = bend
281 ib = bend
282
282
283 def minimize(self, merge_regions):
283 def minimize(self, merge_regions):
284 """Trim conflict regions of lines where A and B sides match.
284 """Trim conflict regions of lines where A and B sides match.
285
285
286 Lines where both A and B have made the same changes at the beginning
286 Lines where both A and B have made the same changes at the beginning
287 or the end of each merge region are eliminated from the conflict
287 or the end of each merge region are eliminated from the conflict
288 region and are instead considered the same.
288 region and are instead considered the same.
289 """
289 """
290 for region in merge_regions:
290 for region in merge_regions:
291 if region[0] != b"conflict":
291 if region[0] != b"conflict":
292 yield region
292 yield region
293 continue
293 continue
294 issue, z1, z2, a1, a2, b1, b2 = region
294 # pytype thinks this tuple contains only 3 things, but
295 # that's clearly not true because this code successfully
296 # executes. It might be wise to rework merge_regions to be
297 # some kind of attrs type.
298 (
299 issue,
300 z1,
301 z2,
302 a1,
303 a2,
304 b1,
305 b2,
306 ) = region # pytype: disable=bad-unpacking
295 alen = a2 - a1
307 alen = a2 - a1
296 blen = b2 - b1
308 blen = b2 - b1
297
309
298 # find matches at the front
310 # find matches at the front
299 ii = 0
311 ii = 0
300 while (
312 while (
301 ii < alen and ii < blen and self.a[a1 + ii] == self.b[b1 + ii]
313 ii < alen and ii < blen and self.a[a1 + ii] == self.b[b1 + ii]
302 ):
314 ):
303 ii += 1
315 ii += 1
304 startmatches = ii
316 startmatches = ii
305
317
306 # find matches at the end
318 # find matches at the end
307 ii = 0
319 ii = 0
308 while (
320 while (
309 ii < alen
321 ii < alen
310 and ii < blen
322 and ii < blen
311 and self.a[a2 - ii - 1] == self.b[b2 - ii - 1]
323 and self.a[a2 - ii - 1] == self.b[b2 - ii - 1]
312 ):
324 ):
313 ii += 1
325 ii += 1
314 endmatches = ii
326 endmatches = ii
315
327
316 if startmatches > 0:
328 if startmatches > 0:
317 yield b'same', a1, a1 + startmatches
329 yield b'same', a1, a1 + startmatches
318
330
319 yield (
331 yield (
320 b'conflict',
332 b'conflict',
321 z1,
333 z1,
322 z2,
334 z2,
323 a1 + startmatches,
335 a1 + startmatches,
324 a2 - endmatches,
336 a2 - endmatches,
325 b1 + startmatches,
337 b1 + startmatches,
326 b2 - endmatches,
338 b2 - endmatches,
327 )
339 )
328
340
329 if endmatches > 0:
341 if endmatches > 0:
330 yield b'same', a2 - endmatches, a2
342 yield b'same', a2 - endmatches, a2
331
343
332 def find_sync_regions(self):
344 def find_sync_regions(self):
333 """Return a list of sync regions, where both descendants match the base.
345 """Return a list of sync regions, where both descendants match the base.
334
346
335 Generates a list of (base1, base2, a1, a2, b1, b2). There is
347 Generates a list of (base1, base2, a1, a2, b1, b2). There is
336 always a zero-length sync region at the end of all the files.
348 always a zero-length sync region at the end of all the files.
337 """
349 """
338
350
339 ia = ib = 0
351 ia = ib = 0
340 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
352 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
341 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
353 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
342 len_a = len(amatches)
354 len_a = len(amatches)
343 len_b = len(bmatches)
355 len_b = len(bmatches)
344
356
345 sl = []
357 sl = []
346
358
347 while ia < len_a and ib < len_b:
359 while ia < len_a and ib < len_b:
348 abase, amatch, alen = amatches[ia]
360 abase, amatch, alen = amatches[ia]
349 bbase, bmatch, blen = bmatches[ib]
361 bbase, bmatch, blen = bmatches[ib]
350
362
351 # there is an unconflicted block at i; how long does it
363 # there is an unconflicted block at i; how long does it
352 # extend? until whichever one ends earlier.
364 # extend? until whichever one ends earlier.
353 i = intersect((abase, abase + alen), (bbase, bbase + blen))
365 i = intersect((abase, abase + alen), (bbase, bbase + blen))
354 if i:
366 if i:
355 intbase = i[0]
367 intbase = i[0]
356 intend = i[1]
368 intend = i[1]
357 intlen = intend - intbase
369 intlen = intend - intbase
358
370
359 # found a match of base[i[0], i[1]]; this may be less than
371 # found a match of base[i[0], i[1]]; this may be less than
360 # the region that matches in either one
372 # the region that matches in either one
361 assert intlen <= alen
373 assert intlen <= alen
362 assert intlen <= blen
374 assert intlen <= blen
363 assert abase <= intbase
375 assert abase <= intbase
364 assert bbase <= intbase
376 assert bbase <= intbase
365
377
366 asub = amatch + (intbase - abase)
378 asub = amatch + (intbase - abase)
367 bsub = bmatch + (intbase - bbase)
379 bsub = bmatch + (intbase - bbase)
368 aend = asub + intlen
380 aend = asub + intlen
369 bend = bsub + intlen
381 bend = bsub + intlen
370
382
371 assert self.base[intbase:intend] == self.a[asub:aend], (
383 assert self.base[intbase:intend] == self.a[asub:aend], (
372 self.base[intbase:intend],
384 self.base[intbase:intend],
373 self.a[asub:aend],
385 self.a[asub:aend],
374 )
386 )
375
387
376 assert self.base[intbase:intend] == self.b[bsub:bend]
388 assert self.base[intbase:intend] == self.b[bsub:bend]
377
389
378 sl.append((intbase, intend, asub, aend, bsub, bend))
390 sl.append((intbase, intend, asub, aend, bsub, bend))
379
391
380 # advance whichever one ends first in the base text
392 # advance whichever one ends first in the base text
381 if (abase + alen) < (bbase + blen):
393 if (abase + alen) < (bbase + blen):
382 ia += 1
394 ia += 1
383 else:
395 else:
384 ib += 1
396 ib += 1
385
397
386 intbase = len(self.base)
398 intbase = len(self.base)
387 abase = len(self.a)
399 abase = len(self.a)
388 bbase = len(self.b)
400 bbase = len(self.b)
389 sl.append((intbase, intbase, abase, abase, bbase, bbase))
401 sl.append((intbase, intbase, abase, abase, bbase, bbase))
390
402
391 return sl
403 return sl
392
404
393 def find_unconflicted(self):
405 def find_unconflicted(self):
394 """Return a list of ranges in base that are not conflicted."""
406 """Return a list of ranges in base that are not conflicted."""
395 am = mdiff.get_matching_blocks(self.basetext, self.atext)
407 am = mdiff.get_matching_blocks(self.basetext, self.atext)
396 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
408 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
397
409
398 unc = []
410 unc = []
399
411
400 while am and bm:
412 while am and bm:
401 # there is an unconflicted block at i; how long does it
413 # there is an unconflicted block at i; how long does it
402 # extend? until whichever one ends earlier.
414 # extend? until whichever one ends earlier.
403 a1 = am[0][0]
415 a1 = am[0][0]
404 a2 = a1 + am[0][2]
416 a2 = a1 + am[0][2]
405 b1 = bm[0][0]
417 b1 = bm[0][0]
406 b2 = b1 + bm[0][2]
418 b2 = b1 + bm[0][2]
407 i = intersect((a1, a2), (b1, b2))
419 i = intersect((a1, a2), (b1, b2))
408 if i:
420 if i:
409 unc.append(i)
421 unc.append(i)
410
422
411 if a2 < b2:
423 if a2 < b2:
412 del am[0]
424 del am[0]
413 else:
425 else:
414 del bm[0]
426 del bm[0]
415
427
416 return unc
428 return unc
417
429
418
430
419 def _verifytext(text, path, ui, opts):
431 def _verifytext(text, path, ui, opts):
420 """verifies that text is non-binary (unless opts[text] is passed,
432 """verifies that text is non-binary (unless opts[text] is passed,
421 then we just warn)"""
433 then we just warn)"""
422 if stringutil.binary(text):
434 if stringutil.binary(text):
423 msg = _(b"%s looks like a binary file.") % path
435 msg = _(b"%s looks like a binary file.") % path
424 if not opts.get(b'quiet'):
436 if not opts.get(b'quiet'):
425 ui.warn(_(b'warning: %s\n') % msg)
437 ui.warn(_(b'warning: %s\n') % msg)
426 if not opts.get(b'text'):
438 if not opts.get(b'text'):
427 raise error.Abort(msg)
439 raise error.Abort(msg)
428 return text
440 return text
429
441
430
442
431 def _picklabels(defaults, overrides):
443 def _picklabels(defaults, overrides):
432 if len(overrides) > 3:
444 if len(overrides) > 3:
433 raise error.Abort(_(b"can only specify three labels."))
445 raise error.Abort(_(b"can only specify three labels."))
434 result = defaults[:]
446 result = defaults[:]
435 for i, override in enumerate(overrides):
447 for i, override in enumerate(overrides):
436 result[i] = override
448 result[i] = override
437 return result
449 return result
438
450
439
451
440 def simplemerge(ui, localctx, basectx, otherctx, **opts):
452 def simplemerge(ui, localctx, basectx, otherctx, **opts):
441 """Performs the simplemerge algorithm.
453 """Performs the simplemerge algorithm.
442
454
443 The merged result is written into `localctx`.
455 The merged result is written into `localctx`.
444 """
456 """
445 opts = pycompat.byteskwargs(opts)
457 opts = pycompat.byteskwargs(opts)
446
458
447 def readctx(ctx):
459 def readctx(ctx):
448 # Merges were always run in the working copy before, which means
460 # Merges were always run in the working copy before, which means
449 # they used decoded data, if the user defined any repository
461 # they used decoded data, if the user defined any repository
450 # filters.
462 # filters.
451 #
463 #
452 # Maintain that behavior today for BC, though perhaps in the future
464 # Maintain that behavior today for BC, though perhaps in the future
453 # it'd be worth considering whether merging encoded data (what the
465 # it'd be worth considering whether merging encoded data (what the
454 # repository usually sees) might be more useful.
466 # repository usually sees) might be more useful.
455 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
467 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
456
468
457 mode = opts.get(b'mode', b'merge')
469 mode = opts.get(b'mode', b'merge')
458 name_a, name_b, name_base = None, None, None
470 name_a, name_b, name_base = None, None, None
459 if mode != b'union':
471 if mode != b'union':
460 name_a, name_b, name_base = _picklabels(
472 name_a, name_b, name_base = _picklabels(
461 [localctx.path(), otherctx.path(), None], opts.get(b'label', [])
473 [localctx.path(), otherctx.path(), None], opts.get(b'label', [])
462 )
474 )
463
475
464 try:
476 try:
465 localtext = readctx(localctx)
477 localtext = readctx(localctx)
466 basetext = readctx(basectx)
478 basetext = readctx(basectx)
467 othertext = readctx(otherctx)
479 othertext = readctx(otherctx)
468 except error.Abort:
480 except error.Abort:
469 return 1
481 return 1
470
482
471 m3 = Merge3Text(basetext, localtext, othertext)
483 m3 = Merge3Text(basetext, localtext, othertext)
472 extrakwargs = {
484 extrakwargs = {
473 b"localorother": opts.get(b"localorother", None),
485 b"localorother": opts.get(b"localorother", None),
474 b'minimize': True,
486 b'minimize': True,
475 }
487 }
476 if mode == b'union':
488 if mode == b'union':
477 extrakwargs[b'start_marker'] = None
489 extrakwargs[b'start_marker'] = None
478 extrakwargs[b'mid_marker'] = None
490 extrakwargs[b'mid_marker'] = None
479 extrakwargs[b'end_marker'] = None
491 extrakwargs[b'end_marker'] = None
480 elif name_base is not None:
492 elif name_base is not None:
481 extrakwargs[b'base_marker'] = b'|||||||'
493 extrakwargs[b'base_marker'] = b'|||||||'
482 extrakwargs[b'name_base'] = name_base
494 extrakwargs[b'name_base'] = name_base
483 extrakwargs[b'minimize'] = False
495 extrakwargs[b'minimize'] = False
484
496
485 mergedtext = b""
497 mergedtext = b""
486 for line in m3.merge_lines(
498 for line in m3.merge_lines(
487 name_a=name_a, name_b=name_b, **pycompat.strkwargs(extrakwargs)
499 name_a=name_a, name_b=name_b, **pycompat.strkwargs(extrakwargs)
488 ):
500 ):
489 if opts.get(b'print'):
501 if opts.get(b'print'):
490 ui.fout.write(line)
502 ui.fout.write(line)
491 else:
503 else:
492 mergedtext += line
504 mergedtext += line
493
505
494 if not opts.get(b'print'):
506 if not opts.get(b'print'):
495 localctx.write(mergedtext, localctx.flags())
507 localctx.write(mergedtext, localctx.flags())
496
508
497 if m3.conflicts and not mode == b'union':
509 if m3.conflicts and not mode == b'union':
498 return 1
510 return 1
General Comments 0
You need to be logged in to leave comments. Login now