##// END OF EJS Templates
merge: introduce method to minimize merge regions...
Ryan McElroy -
r28071:261324dd default
parent child Browse files
Show More
@@ -1,420 +1,459 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 import os
21 import os
22 import sys
22 import sys
23
23
24 from .i18n import _
24 from .i18n import _
25 from . import (
25 from . import (
26 error,
26 error,
27 mdiff,
27 mdiff,
28 scmutil,
28 scmutil,
29 util,
29 util,
30 )
30 )
31
31
32 class CantReprocessAndShowBase(Exception):
32 class CantReprocessAndShowBase(Exception):
33 pass
33 pass
34
34
35 def intersect(ra, rb):
35 def intersect(ra, rb):
36 """Given two ranges return the range where they intersect or None.
36 """Given two ranges return the range where they intersect or None.
37
37
38 >>> intersect((0, 10), (0, 6))
38 >>> intersect((0, 10), (0, 6))
39 (0, 6)
39 (0, 6)
40 >>> intersect((0, 10), (5, 15))
40 >>> intersect((0, 10), (5, 15))
41 (5, 10)
41 (5, 10)
42 >>> intersect((0, 10), (10, 15))
42 >>> intersect((0, 10), (10, 15))
43 >>> intersect((0, 9), (10, 15))
43 >>> intersect((0, 9), (10, 15))
44 >>> intersect((0, 9), (7, 15))
44 >>> intersect((0, 9), (7, 15))
45 (7, 9)
45 (7, 9)
46 """
46 """
47 assert ra[0] <= ra[1]
47 assert ra[0] <= ra[1]
48 assert rb[0] <= rb[1]
48 assert rb[0] <= rb[1]
49
49
50 sa = max(ra[0], rb[0])
50 sa = max(ra[0], rb[0])
51 sb = min(ra[1], rb[1])
51 sb = min(ra[1], rb[1])
52 if sa < sb:
52 if sa < sb:
53 return sa, sb
53 return sa, sb
54 else:
54 else:
55 return None
55 return None
56
56
57 def compare_range(a, astart, aend, b, bstart, bend):
57 def compare_range(a, astart, aend, b, bstart, bend):
58 """Compare a[astart:aend] == b[bstart:bend], without slicing.
58 """Compare a[astart:aend] == b[bstart:bend], without slicing.
59 """
59 """
60 if (aend - astart) != (bend - bstart):
60 if (aend - astart) != (bend - bstart):
61 return False
61 return False
62 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
62 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
63 if a[ia] != b[ib]:
63 if a[ia] != b[ib]:
64 return False
64 return False
65 else:
65 else:
66 return True
66 return True
67
67
68 class Merge3Text(object):
68 class Merge3Text(object):
69 """3-way merge of texts.
69 """3-way merge of texts.
70
70
71 Given strings BASE, OTHER, THIS, tries to produce a combined text
71 Given strings BASE, OTHER, THIS, tries to produce a combined text
72 incorporating the changes from both BASE->OTHER and BASE->THIS."""
72 incorporating the changes from both BASE->OTHER and BASE->THIS."""
73 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
73 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
74 self.basetext = basetext
74 self.basetext = basetext
75 self.atext = atext
75 self.atext = atext
76 self.btext = btext
76 self.btext = btext
77 if base is None:
77 if base is None:
78 base = mdiff.splitnewlines(basetext)
78 base = mdiff.splitnewlines(basetext)
79 if a is None:
79 if a is None:
80 a = mdiff.splitnewlines(atext)
80 a = mdiff.splitnewlines(atext)
81 if b is None:
81 if b is None:
82 b = mdiff.splitnewlines(btext)
82 b = mdiff.splitnewlines(btext)
83 self.base = base
83 self.base = base
84 self.a = a
84 self.a = a
85 self.b = b
85 self.b = b
86
86
87 def merge_lines(self,
87 def merge_lines(self,
88 name_a=None,
88 name_a=None,
89 name_b=None,
89 name_b=None,
90 name_base=None,
90 name_base=None,
91 start_marker='<<<<<<<',
91 start_marker='<<<<<<<',
92 mid_marker='=======',
92 mid_marker='=======',
93 end_marker='>>>>>>>',
93 end_marker='>>>>>>>',
94 base_marker=None,
94 base_marker=None,
95 localorother=None):
95 localorother=None):
96 """Return merge in cvs-like form.
96 """Return merge in cvs-like form.
97 """
97 """
98 self.conflicts = False
98 self.conflicts = False
99 newline = '\n'
99 newline = '\n'
100 if len(self.a) > 0:
100 if len(self.a) > 0:
101 if self.a[0].endswith('\r\n'):
101 if self.a[0].endswith('\r\n'):
102 newline = '\r\n'
102 newline = '\r\n'
103 elif self.a[0].endswith('\r'):
103 elif self.a[0].endswith('\r'):
104 newline = '\r'
104 newline = '\r'
105 if name_a and start_marker:
105 if name_a and start_marker:
106 start_marker = start_marker + ' ' + name_a
106 start_marker = start_marker + ' ' + name_a
107 if name_b and end_marker:
107 if name_b and end_marker:
108 end_marker = end_marker + ' ' + name_b
108 end_marker = end_marker + ' ' + name_b
109 if name_base and base_marker:
109 if name_base and base_marker:
110 base_marker = base_marker + ' ' + name_base
110 base_marker = base_marker + ' ' + name_base
111 merge_regions = self.merge_regions()
111 merge_regions = self.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):
273 """Trim conflict regions of lines where A and B sides match.
274
275 Lines where both A and B have made the same changes at the begining
276 or the end of each merge region are eliminated from the conflict
277 region and are instead considered the same.
278 """
279 for region in merge_regions:
280 if region[0] != "conflict":
281 yield region
282 continue
283 issue, z1, z2, a1, a2, b1, b2 = region
284 alen = a2 - a1
285 blen = b2 - b1
286
287 # find matches at the front
288 ii = 0
289 while ii < alen and ii < blen and \
290 self.a[a1 + ii] == self.b[b1 + ii]:
291 ii += 1
292 startmatches = ii
293
294 # find matches at the end
295 ii = 0
296 while ii < alen and ii < blen and \
297 self.a[a2 - ii - 1] == self.b[b2 - ii - 1]:
298 ii += 1
299 endmatches = ii
300
301 if startmatches > 0:
302 yield 'same', a1, a1 + startmatches
303
304 yield ('conflict', z1, z2,
305 a1 + startmatches, a2 - endmatches,
306 b1 + startmatches, b2 - endmatches)
307
308 if endmatches > 0:
309 yield 'same', a2 - endmatches, a2
310
272 def find_sync_regions(self):
311 def find_sync_regions(self):
273 """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.
274
313
275 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
276 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.
277 """
316 """
278
317
279 ia = ib = 0
318 ia = ib = 0
280 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
319 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
281 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
320 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
282 len_a = len(amatches)
321 len_a = len(amatches)
283 len_b = len(bmatches)
322 len_b = len(bmatches)
284
323
285 sl = []
324 sl = []
286
325
287 while ia < len_a and ib < len_b:
326 while ia < len_a and ib < len_b:
288 abase, amatch, alen = amatches[ia]
327 abase, amatch, alen = amatches[ia]
289 bbase, bmatch, blen = bmatches[ib]
328 bbase, bmatch, blen = bmatches[ib]
290
329
291 # there is an unconflicted block at i; how long does it
330 # there is an unconflicted block at i; how long does it
292 # extend? until whichever one ends earlier.
331 # extend? until whichever one ends earlier.
293 i = intersect((abase, abase + alen), (bbase, bbase + blen))
332 i = intersect((abase, abase + alen), (bbase, bbase + blen))
294 if i:
333 if i:
295 intbase = i[0]
334 intbase = i[0]
296 intend = i[1]
335 intend = i[1]
297 intlen = intend - intbase
336 intlen = intend - intbase
298
337
299 # 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
300 # the region that matches in either one
339 # the region that matches in either one
301 assert intlen <= alen
340 assert intlen <= alen
302 assert intlen <= blen
341 assert intlen <= blen
303 assert abase <= intbase
342 assert abase <= intbase
304 assert bbase <= intbase
343 assert bbase <= intbase
305
344
306 asub = amatch + (intbase - abase)
345 asub = amatch + (intbase - abase)
307 bsub = bmatch + (intbase - bbase)
346 bsub = bmatch + (intbase - bbase)
308 aend = asub + intlen
347 aend = asub + intlen
309 bend = bsub + intlen
348 bend = bsub + intlen
310
349
311 assert self.base[intbase:intend] == self.a[asub:aend], \
350 assert self.base[intbase:intend] == self.a[asub:aend], \
312 (self.base[intbase:intend], self.a[asub:aend])
351 (self.base[intbase:intend], self.a[asub:aend])
313
352
314 assert self.base[intbase:intend] == self.b[bsub:bend]
353 assert self.base[intbase:intend] == self.b[bsub:bend]
315
354
316 sl.append((intbase, intend,
355 sl.append((intbase, intend,
317 asub, aend,
356 asub, aend,
318 bsub, bend))
357 bsub, bend))
319
358
320 # advance whichever one ends first in the base text
359 # advance whichever one ends first in the base text
321 if (abase + alen) < (bbase + blen):
360 if (abase + alen) < (bbase + blen):
322 ia += 1
361 ia += 1
323 else:
362 else:
324 ib += 1
363 ib += 1
325
364
326 intbase = len(self.base)
365 intbase = len(self.base)
327 abase = len(self.a)
366 abase = len(self.a)
328 bbase = len(self.b)
367 bbase = len(self.b)
329 sl.append((intbase, intbase, abase, abase, bbase, bbase))
368 sl.append((intbase, intbase, abase, abase, bbase, bbase))
330
369
331 return sl
370 return sl
332
371
333 def find_unconflicted(self):
372 def find_unconflicted(self):
334 """Return a list of ranges in base that are not conflicted."""
373 """Return a list of ranges in base that are not conflicted."""
335 am = mdiff.get_matching_blocks(self.basetext, self.atext)
374 am = mdiff.get_matching_blocks(self.basetext, self.atext)
336 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
375 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
337
376
338 unc = []
377 unc = []
339
378
340 while am and bm:
379 while am and bm:
341 # there is an unconflicted block at i; how long does it
380 # there is an unconflicted block at i; how long does it
342 # extend? until whichever one ends earlier.
381 # extend? until whichever one ends earlier.
343 a1 = am[0][0]
382 a1 = am[0][0]
344 a2 = a1 + am[0][2]
383 a2 = a1 + am[0][2]
345 b1 = bm[0][0]
384 b1 = bm[0][0]
346 b2 = b1 + bm[0][2]
385 b2 = b1 + bm[0][2]
347 i = intersect((a1, a2), (b1, b2))
386 i = intersect((a1, a2), (b1, b2))
348 if i:
387 if i:
349 unc.append(i)
388 unc.append(i)
350
389
351 if a2 < b2:
390 if a2 < b2:
352 del am[0]
391 del am[0]
353 else:
392 else:
354 del bm[0]
393 del bm[0]
355
394
356 return unc
395 return unc
357
396
358 def simplemerge(ui, local, base, other, **opts):
397 def simplemerge(ui, local, base, other, **opts):
359 def readfile(filename):
398 def readfile(filename):
360 f = open(filename, "rb")
399 f = open(filename, "rb")
361 text = f.read()
400 text = f.read()
362 f.close()
401 f.close()
363 if util.binary(text):
402 if util.binary(text):
364 msg = _("%s looks like a binary file.") % filename
403 msg = _("%s looks like a binary file.") % filename
365 if not opts.get('quiet'):
404 if not opts.get('quiet'):
366 ui.warn(_('warning: %s\n') % msg)
405 ui.warn(_('warning: %s\n') % msg)
367 if not opts.get('text'):
406 if not opts.get('text'):
368 raise error.Abort(msg)
407 raise error.Abort(msg)
369 return text
408 return text
370
409
371 mode = opts.get('mode','merge')
410 mode = opts.get('mode','merge')
372 if mode == 'union':
411 if mode == 'union':
373 name_a = None
412 name_a = None
374 name_b = None
413 name_b = None
375 name_base = None
414 name_base = None
376 else:
415 else:
377 name_a = local
416 name_a = local
378 name_b = other
417 name_b = other
379 name_base = None
418 name_base = None
380 labels = opts.get('label', [])
419 labels = opts.get('label', [])
381 if len(labels) > 0:
420 if len(labels) > 0:
382 name_a = labels[0]
421 name_a = labels[0]
383 if len(labels) > 1:
422 if len(labels) > 1:
384 name_b = labels[1]
423 name_b = labels[1]
385 if len(labels) > 2:
424 if len(labels) > 2:
386 name_base = labels[2]
425 name_base = labels[2]
387 if len(labels) > 3:
426 if len(labels) > 3:
388 raise error.Abort(_("can only specify three labels."))
427 raise error.Abort(_("can only specify three labels."))
389
428
390 try:
429 try:
391 localtext = readfile(local)
430 localtext = readfile(local)
392 basetext = readfile(base)
431 basetext = readfile(base)
393 othertext = readfile(other)
432 othertext = readfile(other)
394 except error.Abort:
433 except error.Abort:
395 return 1
434 return 1
396
435
397 local = os.path.realpath(local)
436 local = os.path.realpath(local)
398 if not opts.get('print'):
437 if not opts.get('print'):
399 opener = scmutil.opener(os.path.dirname(local))
438 opener = scmutil.opener(os.path.dirname(local))
400 out = opener(os.path.basename(local), "w", atomictemp=True)
439 out = opener(os.path.basename(local), "w", atomictemp=True)
401 else:
440 else:
402 out = sys.stdout
441 out = sys.stdout
403
442
404 m3 = Merge3Text(basetext, localtext, othertext)
443 m3 = Merge3Text(basetext, localtext, othertext)
405 extrakwargs = {"localorother": opts.get("localorother", None)}
444 extrakwargs = {"localorother": opts.get("localorother", None)}
406 if mode == 'union':
445 if mode == 'union':
407 extrakwargs['start_marker'] = None
446 extrakwargs['start_marker'] = None
408 extrakwargs['mid_marker'] = None
447 extrakwargs['mid_marker'] = None
409 extrakwargs['end_marker'] = None
448 extrakwargs['end_marker'] = None
410 elif name_base is not None:
449 elif name_base is not None:
411 extrakwargs['base_marker'] = '|||||||'
450 extrakwargs['base_marker'] = '|||||||'
412 extrakwargs['name_base'] = name_base
451 extrakwargs['name_base'] = name_base
413 for line in m3.merge_lines(name_a=name_a, name_b=name_b, **extrakwargs):
452 for line in m3.merge_lines(name_a=name_a, name_b=name_b, **extrakwargs):
414 out.write(line)
453 out.write(line)
415
454
416 if not opts.get('print'):
455 if not opts.get('print'):
417 out.close()
456 out.close()
418
457
419 if m3.conflicts and not mode == 'union':
458 if m3.conflicts and not mode == 'union':
420 return 1
459 return 1
General Comments 0
You need to be logged in to leave comments. Login now