##// END OF EJS Templates
simplemerge: use same newline detection for :mergediff as for :merge[3]...
Martin von Zweigbergk -
r49391:2c802d46 default draft
parent child Browse files
Show More
@@ -1,525 +1,530 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(defaults, overrides):
335 def _picklabels(defaults, 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 = defaults[:]
338 result = defaults[:]
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):
345 if len(m3.a) > 0:
346 if m3.a[0].endswith(b'\r\n'):
347 return b'\r\n'
348 elif m3.a[0].endswith(b'\r'):
349 return b'\r'
350 return b'\n'
351
352
344 def render_markers(
353 def render_markers(
345 m3,
354 m3,
346 name_a=None,
355 name_a=None,
347 name_b=None,
356 name_b=None,
348 name_base=None,
357 name_base=None,
349 start_marker=b'<<<<<<<',
358 start_marker=b'<<<<<<<',
350 mid_marker=b'=======',
359 mid_marker=b'=======',
351 end_marker=b'>>>>>>>',
360 end_marker=b'>>>>>>>',
352 base_marker=None,
361 base_marker=None,
353 minimize=False,
362 minimize=False,
354 ):
363 ):
355 """Return merge in cvs-like form."""
364 """Return merge in cvs-like form."""
365 newline = _detect_newline(m3)
356 conflicts = False
366 conflicts = False
357 newline = b'\n'
358 if len(m3.a) > 0:
359 if m3.a[0].endswith(b'\r\n'):
360 newline = b'\r\n'
361 elif m3.a[0].endswith(b'\r'):
362 newline = b'\r'
363 if name_a and start_marker:
367 if name_a and start_marker:
364 start_marker = start_marker + b' ' + name_a
368 start_marker = start_marker + b' ' + name_a
365 if name_b and end_marker:
369 if name_b and end_marker:
366 end_marker = end_marker + b' ' + name_b
370 end_marker = end_marker + b' ' + name_b
367 if name_base and base_marker:
371 if name_base and base_marker:
368 base_marker = base_marker + b' ' + name_base
372 base_marker = base_marker + b' ' + name_base
369 merge_groups = m3.merge_groups()
373 merge_groups = m3.merge_groups()
370 if minimize:
374 if minimize:
371 merge_groups = m3.minimize(merge_groups)
375 merge_groups = m3.minimize(merge_groups)
372 lines = []
376 lines = []
373 for what, group_lines in merge_groups:
377 for what, group_lines in merge_groups:
374 if what == b'conflict':
378 if what == b'conflict':
375 base_lines, a_lines, b_lines = group_lines
379 base_lines, a_lines, b_lines = group_lines
376 conflicts = True
380 conflicts = True
377 if start_marker is not None:
381 if start_marker is not None:
378 lines.append(start_marker + newline)
382 lines.append(start_marker + newline)
379 lines.extend(a_lines)
383 lines.extend(a_lines)
380 if base_marker is not None:
384 if base_marker is not None:
381 lines.append(base_marker + newline)
385 lines.append(base_marker + newline)
382 lines.extend(base_lines)
386 lines.extend(base_lines)
383 if mid_marker is not None:
387 if mid_marker is not None:
384 lines.append(mid_marker + newline)
388 lines.append(mid_marker + newline)
385 lines.extend(b_lines)
389 lines.extend(b_lines)
386 if end_marker is not None:
390 if end_marker is not None:
387 lines.append(end_marker + newline)
391 lines.append(end_marker + newline)
388 else:
392 else:
389 lines.extend(group_lines)
393 lines.extend(group_lines)
390 return lines, conflicts
394 return lines, conflicts
391
395
392
396
393 def render_mergediff(m3, name_a, name_b, name_base):
397 def render_mergediff(m3, name_a, name_b, name_base):
398 newline = _detect_newline(m3)
394 lines = []
399 lines = []
395 conflicts = False
400 conflicts = False
396 for what, group_lines in m3.merge_groups():
401 for what, group_lines in m3.merge_groups():
397 if what == b'conflict':
402 if what == b'conflict':
398 base_lines, a_lines, b_lines = group_lines
403 base_lines, a_lines, b_lines = group_lines
399 base_text = b''.join(base_lines)
404 base_text = b''.join(base_lines)
400 b_blocks = list(
405 b_blocks = list(
401 mdiff.allblocks(
406 mdiff.allblocks(
402 base_text,
407 base_text,
403 b''.join(b_lines),
408 b''.join(b_lines),
404 lines1=base_lines,
409 lines1=base_lines,
405 lines2=b_lines,
410 lines2=b_lines,
406 )
411 )
407 )
412 )
408 a_blocks = list(
413 a_blocks = list(
409 mdiff.allblocks(
414 mdiff.allblocks(
410 base_text,
415 base_text,
411 b''.join(a_lines),
416 b''.join(a_lines),
412 lines1=base_lines,
417 lines1=base_lines,
413 lines2=b_lines,
418 lines2=b_lines,
414 )
419 )
415 )
420 )
416
421
417 def matching_lines(blocks):
422 def matching_lines(blocks):
418 return sum(
423 return sum(
419 block[1] - block[0]
424 block[1] - block[0]
420 for block, kind in blocks
425 for block, kind in blocks
421 if kind == b'='
426 if kind == b'='
422 )
427 )
423
428
424 def diff_lines(blocks, lines1, lines2):
429 def diff_lines(blocks, lines1, lines2):
425 for block, kind in blocks:
430 for block, kind in blocks:
426 if kind == b'=':
431 if kind == b'=':
427 for line in lines1[block[0] : block[1]]:
432 for line in lines1[block[0] : block[1]]:
428 yield b' ' + line
433 yield b' ' + line
429 else:
434 else:
430 for line in lines1[block[0] : block[1]]:
435 for line in lines1[block[0] : block[1]]:
431 yield b'-' + line
436 yield b'-' + line
432 for line in lines2[block[2] : block[3]]:
437 for line in lines2[block[2] : block[3]]:
433 yield b'+' + line
438 yield b'+' + line
434
439
435 lines.append(b"<<<<<<<\n")
440 lines.append(b"<<<<<<<" + newline)
436 if matching_lines(a_blocks) < matching_lines(b_blocks):
441 if matching_lines(a_blocks) < matching_lines(b_blocks):
437 lines.append(b"======= %s\n" % name_a)
442 lines.append(b"======= " + name_a + newline)
438 lines.extend(a_lines)
443 lines.extend(a_lines)
439 lines.append(b"------- %s\n" % name_base)
444 lines.append(b"------- " + name_base + newline)
440 lines.append(b"+++++++ %s\n" % name_b)
445 lines.append(b"+++++++ " + name_b + newline)
441 lines.extend(diff_lines(b_blocks, base_lines, b_lines))
446 lines.extend(diff_lines(b_blocks, base_lines, b_lines))
442 else:
447 else:
443 lines.append(b"------- %s\n" % name_base)
448 lines.append(b"------- " + name_base + newline)
444 lines.append(b"+++++++ %s\n" % name_a)
449 lines.append(b"+++++++ " + name_a + newline)
445 lines.extend(diff_lines(a_blocks, base_lines, a_lines))
450 lines.extend(diff_lines(a_blocks, base_lines, a_lines))
446 lines.append(b"======= %s\n" % name_b)
451 lines.append(b"======= " + name_b + newline)
447 lines.extend(b_lines)
452 lines.extend(b_lines)
448 lines.append(b">>>>>>>\n")
453 lines.append(b">>>>>>>" + newline)
449 conflicts = True
454 conflicts = True
450 else:
455 else:
451 lines.extend(group_lines)
456 lines.extend(group_lines)
452 return lines, conflicts
457 return lines, conflicts
453
458
454
459
455 def _resolve(m3, sides):
460 def _resolve(m3, sides):
456 lines = []
461 lines = []
457 for what, group_lines in m3.merge_groups():
462 for what, group_lines in m3.merge_groups():
458 if what == b'conflict':
463 if what == b'conflict':
459 for side in sides:
464 for side in sides:
460 lines.extend(group_lines[side])
465 lines.extend(group_lines[side])
461 else:
466 else:
462 lines.extend(group_lines)
467 lines.extend(group_lines)
463 return lines
468 return lines
464
469
465
470
466 def simplemerge(ui, localctx, basectx, otherctx, **opts):
471 def simplemerge(ui, localctx, basectx, otherctx, **opts):
467 """Performs the simplemerge algorithm.
472 """Performs the simplemerge algorithm.
468
473
469 The merged result is written into `localctx`.
474 The merged result is written into `localctx`.
470 """
475 """
471
476
472 def readctx(ctx):
477 def readctx(ctx):
473 # Merges were always run in the working copy before, which means
478 # Merges were always run in the working copy before, which means
474 # they used decoded data, if the user defined any repository
479 # they used decoded data, if the user defined any repository
475 # filters.
480 # filters.
476 #
481 #
477 # Maintain that behavior today for BC, though perhaps in the future
482 # Maintain that behavior today for BC, though perhaps in the future
478 # it'd be worth considering whether merging encoded data (what the
483 # it'd be worth considering whether merging encoded data (what the
479 # repository usually sees) might be more useful.
484 # repository usually sees) might be more useful.
480 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
485 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
481
486
482 try:
487 try:
483 localtext = readctx(localctx)
488 localtext = readctx(localctx)
484 basetext = readctx(basectx)
489 basetext = readctx(basectx)
485 othertext = readctx(otherctx)
490 othertext = readctx(otherctx)
486 except error.Abort:
491 except error.Abort:
487 return 1
492 return 1
488
493
489 m3 = Merge3Text(basetext, localtext, othertext)
494 m3 = Merge3Text(basetext, localtext, othertext)
490 conflicts = False
495 conflicts = False
491 mode = opts.get('mode', b'merge')
496 mode = opts.get('mode', b'merge')
492 if mode == b'union':
497 if mode == b'union':
493 lines = _resolve(m3, (1, 2))
498 lines = _resolve(m3, (1, 2))
494 elif mode == b'local':
499 elif mode == b'local':
495 lines = _resolve(m3, (1,))
500 lines = _resolve(m3, (1,))
496 elif mode == b'other':
501 elif mode == b'other':
497 lines = _resolve(m3, (2,))
502 lines = _resolve(m3, (2,))
498 else:
503 else:
499 name_a, name_b, name_base = _picklabels(
504 name_a, name_b, name_base = _picklabels(
500 [localctx.path(), otherctx.path(), None], opts.get('label', [])
505 [localctx.path(), otherctx.path(), None], opts.get('label', [])
501 )
506 )
502 if mode == b'mergediff':
507 if mode == b'mergediff':
503 lines, conflicts = render_mergediff(m3, name_a, name_b, name_base)
508 lines, conflicts = render_mergediff(m3, name_a, name_b, name_base)
504 else:
509 else:
505 extrakwargs = {
510 extrakwargs = {
506 'minimize': True,
511 'minimize': True,
507 }
512 }
508 if mode == b'merge3':
513 if mode == b'merge3':
509 extrakwargs['base_marker'] = b'|||||||'
514 extrakwargs['base_marker'] = b'|||||||'
510 extrakwargs['name_base'] = name_base
515 extrakwargs['name_base'] = name_base
511 extrakwargs['minimize'] = False
516 extrakwargs['minimize'] = False
512 lines, conflicts = render_markers(
517 lines, conflicts = render_markers(
513 m3, name_a=name_a, name_b=name_b, **extrakwargs
518 m3, name_a=name_a, name_b=name_b, **extrakwargs
514 )
519 )
515
520
516 mergedtext = b''.join(lines)
521 mergedtext = b''.join(lines)
517 if opts.get('print'):
522 if opts.get('print'):
518 ui.fout.write(mergedtext)
523 ui.fout.write(mergedtext)
519 else:
524 else:
520 # localctx.flags() already has the merged flags (done in
525 # localctx.flags() already has the merged flags (done in
521 # mergestate.resolve())
526 # mergestate.resolve())
522 localctx.write(mergedtext, localctx.flags())
527 localctx.write(mergedtext, localctx.flags())
523
528
524 if conflicts:
529 if conflicts:
525 return 1
530 return 1
General Comments 0
You need to be logged in to leave comments. Login now