##// END OF EJS Templates
simplemerge: simplify code now that we always write to a context...
Phil Cohen -
r33909:4074de97 default
parent child Browse files
Show More
@@ -1,490 +1,479 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
409 name_a, name_b, name_base = defaults
410
410
411 if len(overrides) > 0:
411 if len(overrides) > 0:
412 name_a = overrides[0]
412 name_a = overrides[0]
413 if len(overrides) > 1:
413 if len(overrides) > 1:
414 name_b = overrides[1]
414 name_b = overrides[1]
415 if len(overrides) > 2:
415 if len(overrides) > 2:
416 name_base = overrides[2]
416 name_base = overrides[2]
417 if len(overrides) > 3:
417 if len(overrides) > 3:
418 raise error.Abort(_("can only specify three labels."))
418 raise error.Abort(_("can only specify three labels."))
419
419
420 return [name_a, name_b, name_base]
420 return [name_a, name_b, name_base]
421
421
422 def simplemerge(ui, localctx, basectx, otherctx, repo=None, **opts):
422 def simplemerge(ui, localctx, basectx, otherctx, repo=None, **opts):
423 """Performs the simplemerge algorithm.
423 """Performs the simplemerge algorithm.
424
424
425 The merged result is written into `localctx`.
425 The merged result is written into `localctx`.
426 """
426 """
427 def readctx(ctx):
427 def readctx(ctx):
428 if not ctx:
428 if not ctx:
429 return None
429 return None
430 # Merges were always run in the working copy before, which means
430 # Merges were always run in the working copy before, which means
431 # they used decoded data, if the user defined any repository
431 # they used decoded data, if the user defined any repository
432 # filters.
432 # filters.
433 #
433 #
434 # Maintain that behavior today for BC, though perhaps in the future
434 # Maintain that behavior today for BC, though perhaps in the future
435 # it'd be worth considering whether merging encoded data (what the
435 # it'd be worth considering whether merging encoded data (what the
436 # repository usually sees) might be more useful.
436 # repository usually sees) might be more useful.
437 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
437 return _verifytext(ctx.decodeddata(), ctx.path(), ui, opts)
438
438
439 class ctxwriter(object):
440 def __init__(self, ctx):
441 self.ctx = ctx
442 self.text = ""
443
444 def write(self, text):
445 self.text += text
446
447 def close(self):
448 self.ctx.write(self.text, self.ctx.flags())
449
450 mode = opts.get('mode','merge')
439 mode = opts.get('mode','merge')
451 name_a, name_b, name_base = None, None, None
440 name_a, name_b, name_base = None, None, None
452 if mode != 'union':
441 if mode != 'union':
453 name_a, name_b, name_base = _picklabels([localctx.path(),
442 name_a, name_b, name_base = _picklabels([localctx.path(),
454 otherctx.path(), None],
443 otherctx.path(), None],
455 opts.get('label', []))
444 opts.get('label', []))
456
445
457 try:
446 try:
458 localtext = readctx(localctx)
447 localtext = readctx(localctx)
459 basetext = readctx(basectx)
448 basetext = readctx(basectx)
460 othertext = readctx(otherctx)
449 othertext = readctx(otherctx)
461 except error.Abort:
450 except error.Abort:
462 return 1
451 return 1
463
452
464 if opts.get('print'):
465 out = ui.fout
466 else:
467 out = ctxwriter(localctx)
468
469 m3 = Merge3Text(basetext, localtext, othertext)
453 m3 = Merge3Text(basetext, localtext, othertext)
470 extrakwargs = {
454 extrakwargs = {
471 "localorother": opts.get("localorother", None),
455 "localorother": opts.get("localorother", None),
472 'minimize': True,
456 'minimize': True,
473 }
457 }
474 if mode == 'union':
458 if mode == 'union':
475 extrakwargs['start_marker'] = None
459 extrakwargs['start_marker'] = None
476 extrakwargs['mid_marker'] = None
460 extrakwargs['mid_marker'] = None
477 extrakwargs['end_marker'] = None
461 extrakwargs['end_marker'] = None
478 elif name_base is not None:
462 elif name_base is not None:
479 extrakwargs['base_marker'] = '|||||||'
463 extrakwargs['base_marker'] = '|||||||'
480 extrakwargs['name_base'] = name_base
464 extrakwargs['name_base'] = name_base
481 extrakwargs['minimize'] = False
465 extrakwargs['minimize'] = False
466
467 mergedtext = ""
482 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
468 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
483 **pycompat.strkwargs(extrakwargs)):
469 **pycompat.strkwargs(extrakwargs)):
484 out.write(line)
470 if opts.get('print'):
471 ui.fout.write(line)
472 else:
473 mergedtext += line
485
474
486 if not opts.get('print'):
475 if not opts.get('print'):
487 out.close()
476 localctx.write(mergedtext, localctx.flags())
488
477
489 if m3.conflicts and not mode == 'union':
478 if m3.conflicts and not mode == 'union':
490 return 1
479 return 1
General Comments 0
You need to be logged in to leave comments. Login now