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