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