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