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