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