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