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