##// END OF EJS Templates
simplemerge: set `conflicts` earlier for :union tool...
Martin von Zweigbergk -
r49254:9e1f174d default
parent child Browse files
Show More
@@ -1,566 +1,566 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 .node import nullrev
22 from .node import nullrev
23 from . import (
23 from . import (
24 error,
24 error,
25 mdiff,
25 mdiff,
26 pycompat,
26 pycompat,
27 util,
27 util,
28 )
28 )
29 from .utils import stringutil
29 from .utils import stringutil
30
30
31
31
32 class CantReprocessAndShowBase(Exception):
32 class CantReprocessAndShowBase(Exception):
33 pass
33 pass
34
34
35
35
36 def intersect(ra, rb):
36 def intersect(ra, rb):
37 """Given two ranges return the range where they intersect or None.
37 """Given two ranges return the range where they intersect or None.
38
38
39 >>> intersect((0, 10), (0, 6))
39 >>> intersect((0, 10), (0, 6))
40 (0, 6)
40 (0, 6)
41 >>> intersect((0, 10), (5, 15))
41 >>> intersect((0, 10), (5, 15))
42 (5, 10)
42 (5, 10)
43 >>> intersect((0, 10), (10, 15))
43 >>> intersect((0, 10), (10, 15))
44 >>> intersect((0, 9), (10, 15))
44 >>> intersect((0, 9), (10, 15))
45 >>> intersect((0, 9), (7, 15))
45 >>> intersect((0, 9), (7, 15))
46 (7, 9)
46 (7, 9)
47 """
47 """
48 assert ra[0] <= ra[1]
48 assert ra[0] <= ra[1]
49 assert rb[0] <= rb[1]
49 assert rb[0] <= rb[1]
50
50
51 sa = max(ra[0], rb[0])
51 sa = max(ra[0], rb[0])
52 sb = min(ra[1], rb[1])
52 sb = min(ra[1], rb[1])
53 if sa < sb:
53 if sa < sb:
54 return sa, sb
54 return sa, sb
55 else:
55 else:
56 return None
56 return None
57
57
58
58
59 def compare_range(a, astart, aend, b, bstart, bend):
59 def compare_range(a, astart, aend, b, bstart, bend):
60 """Compare a[astart:aend] == b[bstart:bend], without slicing."""
60 """Compare a[astart:aend] == b[bstart:bend], without slicing."""
61 if (aend - astart) != (bend - bstart):
61 if (aend - astart) != (bend - bstart):
62 return False
62 return False
63 for ia, ib in zip(
63 for ia, ib in zip(
64 pycompat.xrange(astart, aend), pycompat.xrange(bstart, bend)
64 pycompat.xrange(astart, aend), pycompat.xrange(bstart, bend)
65 ):
65 ):
66 if a[ia] != b[ib]:
66 if a[ia] != b[ib]:
67 return False
67 return False
68 else:
68 else:
69 return True
69 return True
70
70
71
71
72 class Merge3Text(object):
72 class Merge3Text(object):
73 """3-way merge of texts.
73 """3-way merge of texts.
74
74
75 Given strings BASE, OTHER, THIS, tries to produce a combined text
75 Given strings BASE, OTHER, THIS, tries to produce a combined text
76 incorporating the changes from both BASE->OTHER and BASE->THIS."""
76 incorporating the changes from both BASE->OTHER and BASE->THIS."""
77
77
78 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
78 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
79 self.basetext = basetext
79 self.basetext = basetext
80 self.atext = atext
80 self.atext = atext
81 self.btext = btext
81 self.btext = btext
82 if base is None:
82 if base is None:
83 base = mdiff.splitnewlines(basetext)
83 base = mdiff.splitnewlines(basetext)
84 if a is None:
84 if a is None:
85 a = mdiff.splitnewlines(atext)
85 a = mdiff.splitnewlines(atext)
86 if b is None:
86 if b is None:
87 b = mdiff.splitnewlines(btext)
87 b = mdiff.splitnewlines(btext)
88 self.base = base
88 self.base = base
89 self.a = a
89 self.a = a
90 self.b = b
90 self.b = b
91
91
92 def merge_lines(
92 def merge_lines(
93 self,
93 self,
94 name_a=None,
94 name_a=None,
95 name_b=None,
95 name_b=None,
96 name_base=None,
96 name_base=None,
97 start_marker=b'<<<<<<<',
97 start_marker=b'<<<<<<<',
98 mid_marker=b'=======',
98 mid_marker=b'=======',
99 end_marker=b'>>>>>>>',
99 end_marker=b'>>>>>>>',
100 base_marker=None,
100 base_marker=None,
101 localorother=None,
101 localorother=None,
102 minimize=False,
102 minimize=False,
103 ):
103 ):
104 """Return merge in cvs-like form."""
104 """Return merge in cvs-like form."""
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 # pytype thinks this tuple contains only 3 things, but
294 # pytype thinks this tuple contains only 3 things, but
295 # that's clearly not true because this code successfully
295 # that's clearly not true because this code successfully
296 # executes. It might be wise to rework merge_regions to be
296 # executes. It might be wise to rework merge_regions to be
297 # some kind of attrs type.
297 # some kind of attrs type.
298 (
298 (
299 issue,
299 issue,
300 z1,
300 z1,
301 z2,
301 z2,
302 a1,
302 a1,
303 a2,
303 a2,
304 b1,
304 b1,
305 b2,
305 b2,
306 ) = region # pytype: disable=bad-unpacking
306 ) = region # pytype: disable=bad-unpacking
307 alen = a2 - a1
307 alen = a2 - a1
308 blen = b2 - b1
308 blen = b2 - b1
309
309
310 # find matches at the front
310 # find matches at the front
311 ii = 0
311 ii = 0
312 while (
312 while (
313 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]
314 ):
314 ):
315 ii += 1
315 ii += 1
316 startmatches = ii
316 startmatches = ii
317
317
318 # find matches at the end
318 # find matches at the end
319 ii = 0
319 ii = 0
320 while (
320 while (
321 ii < alen
321 ii < alen
322 and ii < blen
322 and ii < blen
323 and self.a[a2 - ii - 1] == self.b[b2 - ii - 1]
323 and self.a[a2 - ii - 1] == self.b[b2 - ii - 1]
324 ):
324 ):
325 ii += 1
325 ii += 1
326 endmatches = ii
326 endmatches = ii
327
327
328 if startmatches > 0:
328 if startmatches > 0:
329 yield b'same', a1, a1 + startmatches
329 yield b'same', a1, a1 + startmatches
330
330
331 yield (
331 yield (
332 b'conflict',
332 b'conflict',
333 z1,
333 z1,
334 z2,
334 z2,
335 a1 + startmatches,
335 a1 + startmatches,
336 a2 - endmatches,
336 a2 - endmatches,
337 b1 + startmatches,
337 b1 + startmatches,
338 b2 - endmatches,
338 b2 - endmatches,
339 )
339 )
340
340
341 if endmatches > 0:
341 if endmatches > 0:
342 yield b'same', a2 - endmatches, a2
342 yield b'same', a2 - endmatches, a2
343
343
344 def find_sync_regions(self):
344 def find_sync_regions(self):
345 """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.
346
346
347 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
348 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.
349 """
349 """
350
350
351 ia = ib = 0
351 ia = ib = 0
352 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
352 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
353 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
353 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
354 len_a = len(amatches)
354 len_a = len(amatches)
355 len_b = len(bmatches)
355 len_b = len(bmatches)
356
356
357 sl = []
357 sl = []
358
358
359 while ia < len_a and ib < len_b:
359 while ia < len_a and ib < len_b:
360 abase, amatch, alen = amatches[ia]
360 abase, amatch, alen = amatches[ia]
361 bbase, bmatch, blen = bmatches[ib]
361 bbase, bmatch, blen = bmatches[ib]
362
362
363 # there is an unconflicted block at i; how long does it
363 # there is an unconflicted block at i; how long does it
364 # extend? until whichever one ends earlier.
364 # extend? until whichever one ends earlier.
365 i = intersect((abase, abase + alen), (bbase, bbase + blen))
365 i = intersect((abase, abase + alen), (bbase, bbase + blen))
366 if i:
366 if i:
367 intbase = i[0]
367 intbase = i[0]
368 intend = i[1]
368 intend = i[1]
369 intlen = intend - intbase
369 intlen = intend - intbase
370
370
371 # 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
372 # the region that matches in either one
372 # the region that matches in either one
373 assert intlen <= alen
373 assert intlen <= alen
374 assert intlen <= blen
374 assert intlen <= blen
375 assert abase <= intbase
375 assert abase <= intbase
376 assert bbase <= intbase
376 assert bbase <= intbase
377
377
378 asub = amatch + (intbase - abase)
378 asub = amatch + (intbase - abase)
379 bsub = bmatch + (intbase - bbase)
379 bsub = bmatch + (intbase - bbase)
380 aend = asub + intlen
380 aend = asub + intlen
381 bend = bsub + intlen
381 bend = bsub + intlen
382
382
383 assert self.base[intbase:intend] == self.a[asub:aend], (
383 assert self.base[intbase:intend] == self.a[asub:aend], (
384 self.base[intbase:intend],
384 self.base[intbase:intend],
385 self.a[asub:aend],
385 self.a[asub:aend],
386 )
386 )
387
387
388 assert self.base[intbase:intend] == self.b[bsub:bend]
388 assert self.base[intbase:intend] == self.b[bsub:bend]
389
389
390 sl.append((intbase, intend, asub, aend, bsub, bend))
390 sl.append((intbase, intend, asub, aend, bsub, bend))
391
391
392 # advance whichever one ends first in the base text
392 # advance whichever one ends first in the base text
393 if (abase + alen) < (bbase + blen):
393 if (abase + alen) < (bbase + blen):
394 ia += 1
394 ia += 1
395 else:
395 else:
396 ib += 1
396 ib += 1
397
397
398 intbase = len(self.base)
398 intbase = len(self.base)
399 abase = len(self.a)
399 abase = len(self.a)
400 bbase = len(self.b)
400 bbase = len(self.b)
401 sl.append((intbase, intbase, abase, abase, bbase, bbase))
401 sl.append((intbase, intbase, abase, abase, bbase, bbase))
402
402
403 return sl
403 return sl
404
404
405
405
406 def _verifytext(text, path, ui, opts):
406 def _verifytext(text, path, ui, opts):
407 """verifies that text is non-binary (unless opts[text] is passed,
407 """verifies that text is non-binary (unless opts[text] is passed,
408 then we just warn)"""
408 then we just warn)"""
409 if stringutil.binary(text):
409 if stringutil.binary(text):
410 msg = _(b"%s looks like a binary file.") % path
410 msg = _(b"%s looks like a binary file.") % path
411 if not opts.get('quiet'):
411 if not opts.get('quiet'):
412 ui.warn(_(b'warning: %s\n') % msg)
412 ui.warn(_(b'warning: %s\n') % msg)
413 if not opts.get('text'):
413 if not opts.get('text'):
414 raise error.Abort(msg)
414 raise error.Abort(msg)
415 return text
415 return text
416
416
417
417
418 def _picklabels(defaults, overrides):
418 def _picklabels(defaults, overrides):
419 if len(overrides) > 3:
419 if len(overrides) > 3:
420 raise error.Abort(_(b"can only specify three labels."))
420 raise error.Abort(_(b"can only specify three labels."))
421 result = defaults[:]
421 result = defaults[:]
422 for i, override in enumerate(overrides):
422 for i, override in enumerate(overrides):
423 result[i] = override
423 result[i] = override
424 return result
424 return result
425
425
426
426
427 def is_not_null(ctx):
427 def is_not_null(ctx):
428 if not util.safehasattr(ctx, "node"):
428 if not util.safehasattr(ctx, "node"):
429 return False
429 return False
430 return ctx.rev() != nullrev
430 return ctx.rev() != nullrev
431
431
432
432
433 def _mergediff(m3, name_a, name_b, name_base):
433 def _mergediff(m3, name_a, name_b, name_base):
434 lines = []
434 lines = []
435 conflicts = False
435 conflicts = False
436 for group in m3.merge_groups():
436 for group in m3.merge_groups():
437 if group[0] == b'conflict':
437 if group[0] == b'conflict':
438 base_lines, a_lines, b_lines = group[1:]
438 base_lines, a_lines, b_lines = group[1:]
439 base_text = b''.join(base_lines)
439 base_text = b''.join(base_lines)
440 b_blocks = list(
440 b_blocks = list(
441 mdiff.allblocks(
441 mdiff.allblocks(
442 base_text,
442 base_text,
443 b''.join(b_lines),
443 b''.join(b_lines),
444 lines1=base_lines,
444 lines1=base_lines,
445 lines2=b_lines,
445 lines2=b_lines,
446 )
446 )
447 )
447 )
448 a_blocks = list(
448 a_blocks = list(
449 mdiff.allblocks(
449 mdiff.allblocks(
450 base_text,
450 base_text,
451 b''.join(a_lines),
451 b''.join(a_lines),
452 lines1=base_lines,
452 lines1=base_lines,
453 lines2=b_lines,
453 lines2=b_lines,
454 )
454 )
455 )
455 )
456
456
457 def matching_lines(blocks):
457 def matching_lines(blocks):
458 return sum(
458 return sum(
459 block[1] - block[0]
459 block[1] - block[0]
460 for block, kind in blocks
460 for block, kind in blocks
461 if kind == b'='
461 if kind == b'='
462 )
462 )
463
463
464 def diff_lines(blocks, lines1, lines2):
464 def diff_lines(blocks, lines1, lines2):
465 for block, kind in blocks:
465 for block, kind in blocks:
466 if kind == b'=':
466 if kind == b'=':
467 for line in lines1[block[0] : block[1]]:
467 for line in lines1[block[0] : block[1]]:
468 yield b' ' + line
468 yield b' ' + line
469 else:
469 else:
470 for line in lines1[block[0] : block[1]]:
470 for line in lines1[block[0] : block[1]]:
471 yield b'-' + line
471 yield b'-' + line
472 for line in lines2[block[2] : block[3]]:
472 for line in lines2[block[2] : block[3]]:
473 yield b'+' + line
473 yield b'+' + line
474
474
475 lines.append(b"<<<<<<<\n")
475 lines.append(b"<<<<<<<\n")
476 if matching_lines(a_blocks) < matching_lines(b_blocks):
476 if matching_lines(a_blocks) < matching_lines(b_blocks):
477 lines.append(b"======= %s\n" % name_a)
477 lines.append(b"======= %s\n" % name_a)
478 lines.extend(a_lines)
478 lines.extend(a_lines)
479 lines.append(b"------- %s\n" % name_base)
479 lines.append(b"------- %s\n" % name_base)
480 lines.append(b"+++++++ %s\n" % name_b)
480 lines.append(b"+++++++ %s\n" % name_b)
481 lines.extend(diff_lines(b_blocks, base_lines, b_lines))
481 lines.extend(diff_lines(b_blocks, base_lines, b_lines))
482 else:
482 else:
483 lines.append(b"------- %s\n" % name_base)
483 lines.append(b"------- %s\n" % name_base)
484 lines.append(b"+++++++ %s\n" % name_a)
484 lines.append(b"+++++++ %s\n" % name_a)
485 lines.extend(diff_lines(a_blocks, base_lines, a_lines))
485 lines.extend(diff_lines(a_blocks, base_lines, a_lines))
486 lines.append(b"======= %s\n" % name_b)
486 lines.append(b"======= %s\n" % name_b)
487 lines.extend(b_lines)
487 lines.extend(b_lines)
488 lines.append(b">>>>>>>\n")
488 lines.append(b">>>>>>>\n")
489 conflicts = True
489 conflicts = True
490 else:
490 else:
491 lines.extend(group[1])
491 lines.extend(group[1])
492 return lines, conflicts
492 return lines, conflicts
493
493
494
494
495 def simplemerge(ui, localctx, basectx, otherctx, **opts):
495 def simplemerge(ui, localctx, basectx, otherctx, **opts):
496 """Performs the simplemerge algorithm.
496 """Performs the simplemerge algorithm.
497
497
498 The merged result is written into `localctx`.
498 The merged result is written into `localctx`.
499 """
499 """
500
500
501 def readctx(ctx):
501 def readctx(ctx):
502 # Merges were always run in the working copy before, which means
502 # Merges were always run in the working copy before, which means
503 # they used decoded data, if the user defined any repository
503 # they used decoded data, if the user defined any repository
504 # filters.
504 # filters.
505 #
505 #
506 # Maintain that behavior today for BC, though perhaps in the future
506 # Maintain that behavior today for BC, though perhaps in the future
507 # it'd be worth considering whether merging encoded data (what the
507 # it'd be worth considering whether merging encoded data (what the
508 # repository usually sees) might be more useful.
508 # repository usually sees) might be more useful.
509 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
509 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
510
510
511 mode = opts.get('mode', b'merge')
511 mode = opts.get('mode', b'merge')
512 name_a, name_b, name_base = None, None, None
512 name_a, name_b, name_base = None, None, None
513 if mode != b'union':
513 if mode != b'union':
514 name_a, name_b, name_base = _picklabels(
514 name_a, name_b, name_base = _picklabels(
515 [localctx.path(), otherctx.path(), None], opts.get('label', [])
515 [localctx.path(), otherctx.path(), None], opts.get('label', [])
516 )
516 )
517
517
518 try:
518 try:
519 localtext = readctx(localctx)
519 localtext = readctx(localctx)
520 basetext = readctx(basectx)
520 basetext = readctx(basectx)
521 othertext = readctx(otherctx)
521 othertext = readctx(otherctx)
522 except error.Abort:
522 except error.Abort:
523 return 1
523 return 1
524
524
525 m3 = Merge3Text(basetext, localtext, othertext)
525 m3 = Merge3Text(basetext, localtext, othertext)
526 extrakwargs = {
526 extrakwargs = {
527 b"localorother": opts.get("localorother", None),
527 b"localorother": opts.get("localorother", None),
528 b'minimize': True,
528 b'minimize': True,
529 }
529 }
530 if mode == b'union':
530 if mode == b'union':
531 extrakwargs[b'start_marker'] = None
531 extrakwargs[b'start_marker'] = None
532 extrakwargs[b'mid_marker'] = None
532 extrakwargs[b'mid_marker'] = None
533 extrakwargs[b'end_marker'] = None
533 extrakwargs[b'end_marker'] = None
534 elif name_base is not None:
534 elif name_base is not None:
535 extrakwargs[b'base_marker'] = b'|||||||'
535 extrakwargs[b'base_marker'] = b'|||||||'
536 extrakwargs[b'name_base'] = name_base
536 extrakwargs[b'name_base'] = name_base
537 extrakwargs[b'minimize'] = False
537 extrakwargs[b'minimize'] = False
538
538
539 if mode == b'mergediff':
539 if mode == b'mergediff':
540 lines, conflicts = _mergediff(m3, name_a, name_b, name_base)
540 lines, conflicts = _mergediff(m3, name_a, name_b, name_base)
541 else:
541 else:
542 lines = list(
542 lines = list(
543 m3.merge_lines(
543 m3.merge_lines(
544 name_a=name_a, name_b=name_b, **pycompat.strkwargs(extrakwargs)
544 name_a=name_a, name_b=name_b, **pycompat.strkwargs(extrakwargs)
545 )
545 )
546 )
546 )
547 conflicts = m3.conflicts
547 conflicts = m3.conflicts and not mode == b'union'
548
548
549 # merge flags if necessary
549 # merge flags if necessary
550 flags = localctx.flags()
550 flags = localctx.flags()
551 localflags = set(pycompat.iterbytestr(flags))
551 localflags = set(pycompat.iterbytestr(flags))
552 otherflags = set(pycompat.iterbytestr(otherctx.flags()))
552 otherflags = set(pycompat.iterbytestr(otherctx.flags()))
553 if is_not_null(basectx) and localflags != otherflags:
553 if is_not_null(basectx) and localflags != otherflags:
554 baseflags = set(pycompat.iterbytestr(basectx.flags()))
554 baseflags = set(pycompat.iterbytestr(basectx.flags()))
555 commonflags = localflags & otherflags
555 commonflags = localflags & otherflags
556 addedflags = (localflags ^ otherflags) - baseflags
556 addedflags = (localflags ^ otherflags) - baseflags
557 flags = b''.join(sorted(commonflags | addedflags))
557 flags = b''.join(sorted(commonflags | addedflags))
558
558
559 mergedtext = b''.join(lines)
559 mergedtext = b''.join(lines)
560 if opts.get('print'):
560 if opts.get('print'):
561 ui.fout.write(mergedtext)
561 ui.fout.write(mergedtext)
562 else:
562 else:
563 localctx.write(mergedtext, flags)
563 localctx.write(mergedtext, flags)
564
564
565 if conflicts and not mode == b'union':
565 if conflicts:
566 return 1
566 return 1
General Comments 0
You need to be logged in to leave comments. Login now