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