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