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