##// END OF EJS Templates
simplemerge: flush stdout before writing to stderr....
Patrick Mezard -
r4408:1ef4445c default
parent child Browse files
Show More
@@ -1,557 +1,562 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # Copyright (C) 2004, 2005 Canonical Ltd
2 # Copyright (C) 2004, 2005 Canonical Ltd
3 #
3 #
4 # This program is free software; you can redistribute it and/or modify
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
7 # (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
18
18
19 # mbp: "you know that thing where cvs gives you conflict markers?"
19 # mbp: "you know that thing where cvs gives you conflict markers?"
20 # s: "i hate that."
20 # s: "i hate that."
21
21
22 from mercurial import demandimport
22 from mercurial import demandimport
23 demandimport.enable()
23 demandimport.enable()
24
24
25 from mercurial import util, mdiff, fancyopts
25 from mercurial import util, mdiff, fancyopts
26 from mercurial.i18n import _
26 from mercurial.i18n import _
27
27
28
28
29 class CantReprocessAndShowBase(Exception):
29 class CantReprocessAndShowBase(Exception):
30 pass
30 pass
31
32
33 def warn(message):
34 sys.stdout.flush()
35 sys.stderr.write(message)
36 sys.stderr.flush()
31
37
32
38
33 def intersect(ra, rb):
39 def intersect(ra, rb):
34 """Given two ranges return the range where they intersect or None.
40 """Given two ranges return the range where they intersect or None.
35
41
36 >>> intersect((0, 10), (0, 6))
42 >>> intersect((0, 10), (0, 6))
37 (0, 6)
43 (0, 6)
38 >>> intersect((0, 10), (5, 15))
44 >>> intersect((0, 10), (5, 15))
39 (5, 10)
45 (5, 10)
40 >>> intersect((0, 10), (10, 15))
46 >>> intersect((0, 10), (10, 15))
41 >>> intersect((0, 9), (10, 15))
47 >>> intersect((0, 9), (10, 15))
42 >>> intersect((0, 9), (7, 15))
48 >>> intersect((0, 9), (7, 15))
43 (7, 9)
49 (7, 9)
44 """
50 """
45 assert ra[0] <= ra[1]
51 assert ra[0] <= ra[1]
46 assert rb[0] <= rb[1]
52 assert rb[0] <= rb[1]
47
53
48 sa = max(ra[0], rb[0])
54 sa = max(ra[0], rb[0])
49 sb = min(ra[1], rb[1])
55 sb = min(ra[1], rb[1])
50 if sa < sb:
56 if sa < sb:
51 return sa, sb
57 return sa, sb
52 else:
58 else:
53 return None
59 return None
54
60
55
61
56 def compare_range(a, astart, aend, b, bstart, bend):
62 def compare_range(a, astart, aend, b, bstart, bend):
57 """Compare a[astart:aend] == b[bstart:bend], without slicing.
63 """Compare a[astart:aend] == b[bstart:bend], without slicing.
58 """
64 """
59 if (aend-astart) != (bend-bstart):
65 if (aend-astart) != (bend-bstart):
60 return False
66 return False
61 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
67 for ia, ib in zip(xrange(astart, aend), xrange(bstart, bend)):
62 if a[ia] != b[ib]:
68 if a[ia] != b[ib]:
63 return False
69 return False
64 else:
70 else:
65 return True
71 return True
66
72
67
73
68
74
69
75
70 class Merge3Text(object):
76 class Merge3Text(object):
71 """3-way merge of texts.
77 """3-way merge of texts.
72
78
73 Given strings BASE, OTHER, THIS, tries to produce a combined text
79 Given strings BASE, OTHER, THIS, tries to produce a combined text
74 incorporating the changes from both BASE->OTHER and BASE->THIS."""
80 incorporating the changes from both BASE->OTHER and BASE->THIS."""
75 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
81 def __init__(self, basetext, atext, btext, base=None, a=None, b=None):
76 self.basetext = basetext
82 self.basetext = basetext
77 self.atext = atext
83 self.atext = atext
78 self.btext = btext
84 self.btext = btext
79 if base is None:
85 if base is None:
80 base = mdiff.splitnewlines(basetext)
86 base = mdiff.splitnewlines(basetext)
81 if a is None:
87 if a is None:
82 a = mdiff.splitnewlines(atext)
88 a = mdiff.splitnewlines(atext)
83 if b is None:
89 if b is None:
84 b = mdiff.splitnewlines(btext)
90 b = mdiff.splitnewlines(btext)
85 self.base = base
91 self.base = base
86 self.a = a
92 self.a = a
87 self.b = b
93 self.b = b
88
94
89
95
90
96
91 def merge_lines(self,
97 def merge_lines(self,
92 name_a=None,
98 name_a=None,
93 name_b=None,
99 name_b=None,
94 name_base=None,
100 name_base=None,
95 start_marker='<<<<<<<',
101 start_marker='<<<<<<<',
96 mid_marker='=======',
102 mid_marker='=======',
97 end_marker='>>>>>>>',
103 end_marker='>>>>>>>',
98 base_marker=None,
104 base_marker=None,
99 reprocess=False):
105 reprocess=False):
100 """Return merge in cvs-like form.
106 """Return merge in cvs-like form.
101 """
107 """
102 self.conflicts = False
108 self.conflicts = False
103 newline = '\n'
109 newline = '\n'
104 if len(self.a) > 0:
110 if len(self.a) > 0:
105 if self.a[0].endswith('\r\n'):
111 if self.a[0].endswith('\r\n'):
106 newline = '\r\n'
112 newline = '\r\n'
107 elif self.a[0].endswith('\r'):
113 elif self.a[0].endswith('\r'):
108 newline = '\r'
114 newline = '\r'
109 if base_marker and reprocess:
115 if base_marker and reprocess:
110 raise CantReprocessAndShowBase()
116 raise CantReprocessAndShowBase()
111 if name_a:
117 if name_a:
112 start_marker = start_marker + ' ' + name_a
118 start_marker = start_marker + ' ' + name_a
113 if name_b:
119 if name_b:
114 end_marker = end_marker + ' ' + name_b
120 end_marker = end_marker + ' ' + name_b
115 if name_base and base_marker:
121 if name_base and base_marker:
116 base_marker = base_marker + ' ' + name_base
122 base_marker = base_marker + ' ' + name_base
117 merge_regions = self.merge_regions()
123 merge_regions = self.merge_regions()
118 if reprocess is True:
124 if reprocess is True:
119 merge_regions = self.reprocess_merge_regions(merge_regions)
125 merge_regions = self.reprocess_merge_regions(merge_regions)
120 for t in merge_regions:
126 for t in merge_regions:
121 what = t[0]
127 what = t[0]
122 if what == 'unchanged':
128 if what == 'unchanged':
123 for i in range(t[1], t[2]):
129 for i in range(t[1], t[2]):
124 yield self.base[i]
130 yield self.base[i]
125 elif what == 'a' or what == 'same':
131 elif what == 'a' or what == 'same':
126 for i in range(t[1], t[2]):
132 for i in range(t[1], t[2]):
127 yield self.a[i]
133 yield self.a[i]
128 elif what == 'b':
134 elif what == 'b':
129 for i in range(t[1], t[2]):
135 for i in range(t[1], t[2]):
130 yield self.b[i]
136 yield self.b[i]
131 elif what == 'conflict':
137 elif what == 'conflict':
132 self.conflicts = True
138 self.conflicts = True
133 yield start_marker + newline
139 yield start_marker + newline
134 for i in range(t[3], t[4]):
140 for i in range(t[3], t[4]):
135 yield self.a[i]
141 yield self.a[i]
136 if base_marker is not None:
142 if base_marker is not None:
137 yield base_marker + newline
143 yield base_marker + newline
138 for i in range(t[1], t[2]):
144 for i in range(t[1], t[2]):
139 yield self.base[i]
145 yield self.base[i]
140 yield mid_marker + newline
146 yield mid_marker + newline
141 for i in range(t[5], t[6]):
147 for i in range(t[5], t[6]):
142 yield self.b[i]
148 yield self.b[i]
143 yield end_marker + newline
149 yield end_marker + newline
144 else:
150 else:
145 raise ValueError(what)
151 raise ValueError(what)
146
152
147
153
148
154
149
155
150
156
151 def merge_annotated(self):
157 def merge_annotated(self):
152 """Return merge with conflicts, showing origin of lines.
158 """Return merge with conflicts, showing origin of lines.
153
159
154 Most useful for debugging merge.
160 Most useful for debugging merge.
155 """
161 """
156 for t in self.merge_regions():
162 for t in self.merge_regions():
157 what = t[0]
163 what = t[0]
158 if what == 'unchanged':
164 if what == 'unchanged':
159 for i in range(t[1], t[2]):
165 for i in range(t[1], t[2]):
160 yield 'u | ' + self.base[i]
166 yield 'u | ' + self.base[i]
161 elif what == 'a' or what == 'same':
167 elif what == 'a' or what == 'same':
162 for i in range(t[1], t[2]):
168 for i in range(t[1], t[2]):
163 yield what[0] + ' | ' + self.a[i]
169 yield what[0] + ' | ' + self.a[i]
164 elif what == 'b':
170 elif what == 'b':
165 for i in range(t[1], t[2]):
171 for i in range(t[1], t[2]):
166 yield 'b | ' + self.b[i]
172 yield 'b | ' + self.b[i]
167 elif what == 'conflict':
173 elif what == 'conflict':
168 yield '<<<<\n'
174 yield '<<<<\n'
169 for i in range(t[3], t[4]):
175 for i in range(t[3], t[4]):
170 yield 'A | ' + self.a[i]
176 yield 'A | ' + self.a[i]
171 yield '----\n'
177 yield '----\n'
172 for i in range(t[5], t[6]):
178 for i in range(t[5], t[6]):
173 yield 'B | ' + self.b[i]
179 yield 'B | ' + self.b[i]
174 yield '>>>>\n'
180 yield '>>>>\n'
175 else:
181 else:
176 raise ValueError(what)
182 raise ValueError(what)
177
183
178
184
179
185
180
186
181
187
182 def merge_groups(self):
188 def merge_groups(self):
183 """Yield sequence of line groups. Each one is a tuple:
189 """Yield sequence of line groups. Each one is a tuple:
184
190
185 'unchanged', lines
191 'unchanged', lines
186 Lines unchanged from base
192 Lines unchanged from base
187
193
188 'a', lines
194 'a', lines
189 Lines taken from a
195 Lines taken from a
190
196
191 'same', lines
197 'same', lines
192 Lines taken from a (and equal to b)
198 Lines taken from a (and equal to b)
193
199
194 'b', lines
200 'b', lines
195 Lines taken from b
201 Lines taken from b
196
202
197 'conflict', base_lines, a_lines, b_lines
203 'conflict', base_lines, a_lines, b_lines
198 Lines from base were changed to either a or b and conflict.
204 Lines from base were changed to either a or b and conflict.
199 """
205 """
200 for t in self.merge_regions():
206 for t in self.merge_regions():
201 what = t[0]
207 what = t[0]
202 if what == 'unchanged':
208 if what == 'unchanged':
203 yield what, self.base[t[1]:t[2]]
209 yield what, self.base[t[1]:t[2]]
204 elif what == 'a' or what == 'same':
210 elif what == 'a' or what == 'same':
205 yield what, self.a[t[1]:t[2]]
211 yield what, self.a[t[1]:t[2]]
206 elif what == 'b':
212 elif what == 'b':
207 yield what, self.b[t[1]:t[2]]
213 yield what, self.b[t[1]:t[2]]
208 elif what == 'conflict':
214 elif what == 'conflict':
209 yield (what,
215 yield (what,
210 self.base[t[1]:t[2]],
216 self.base[t[1]:t[2]],
211 self.a[t[3]:t[4]],
217 self.a[t[3]:t[4]],
212 self.b[t[5]:t[6]])
218 self.b[t[5]:t[6]])
213 else:
219 else:
214 raise ValueError(what)
220 raise ValueError(what)
215
221
216
222
217 def merge_regions(self):
223 def merge_regions(self):
218 """Return sequences of matching and conflicting regions.
224 """Return sequences of matching and conflicting regions.
219
225
220 This returns tuples, where the first value says what kind we
226 This returns tuples, where the first value says what kind we
221 have:
227 have:
222
228
223 'unchanged', start, end
229 'unchanged', start, end
224 Take a region of base[start:end]
230 Take a region of base[start:end]
225
231
226 'same', astart, aend
232 'same', astart, aend
227 b and a are different from base but give the same result
233 b and a are different from base but give the same result
228
234
229 'a', start, end
235 'a', start, end
230 Non-clashing insertion from a[start:end]
236 Non-clashing insertion from a[start:end]
231
237
232 Method is as follows:
238 Method is as follows:
233
239
234 The two sequences align only on regions which match the base
240 The two sequences align only on regions which match the base
235 and both descendents. These are found by doing a two-way diff
241 and both descendents. These are found by doing a two-way diff
236 of each one against the base, and then finding the
242 of each one against the base, and then finding the
237 intersections between those regions. These "sync regions"
243 intersections between those regions. These "sync regions"
238 are by definition unchanged in both and easily dealt with.
244 are by definition unchanged in both and easily dealt with.
239
245
240 The regions in between can be in any of three cases:
246 The regions in between can be in any of three cases:
241 conflicted, or changed on only one side.
247 conflicted, or changed on only one side.
242 """
248 """
243
249
244 # section a[0:ia] has been disposed of, etc
250 # section a[0:ia] has been disposed of, etc
245 iz = ia = ib = 0
251 iz = ia = ib = 0
246
252
247 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
253 for zmatch, zend, amatch, aend, bmatch, bend in self.find_sync_regions():
248 #print 'match base [%d:%d]' % (zmatch, zend)
254 #print 'match base [%d:%d]' % (zmatch, zend)
249
255
250 matchlen = zend - zmatch
256 matchlen = zend - zmatch
251 assert matchlen >= 0
257 assert matchlen >= 0
252 assert matchlen == (aend - amatch)
258 assert matchlen == (aend - amatch)
253 assert matchlen == (bend - bmatch)
259 assert matchlen == (bend - bmatch)
254
260
255 len_a = amatch - ia
261 len_a = amatch - ia
256 len_b = bmatch - ib
262 len_b = bmatch - ib
257 len_base = zmatch - iz
263 len_base = zmatch - iz
258 assert len_a >= 0
264 assert len_a >= 0
259 assert len_b >= 0
265 assert len_b >= 0
260 assert len_base >= 0
266 assert len_base >= 0
261
267
262 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
268 #print 'unmatched a=%d, b=%d' % (len_a, len_b)
263
269
264 if len_a or len_b:
270 if len_a or len_b:
265 # try to avoid actually slicing the lists
271 # try to avoid actually slicing the lists
266 equal_a = compare_range(self.a, ia, amatch,
272 equal_a = compare_range(self.a, ia, amatch,
267 self.base, iz, zmatch)
273 self.base, iz, zmatch)
268 equal_b = compare_range(self.b, ib, bmatch,
274 equal_b = compare_range(self.b, ib, bmatch,
269 self.base, iz, zmatch)
275 self.base, iz, zmatch)
270 same = compare_range(self.a, ia, amatch,
276 same = compare_range(self.a, ia, amatch,
271 self.b, ib, bmatch)
277 self.b, ib, bmatch)
272
278
273 if same:
279 if same:
274 yield 'same', ia, amatch
280 yield 'same', ia, amatch
275 elif equal_a and not equal_b:
281 elif equal_a and not equal_b:
276 yield 'b', ib, bmatch
282 yield 'b', ib, bmatch
277 elif equal_b and not equal_a:
283 elif equal_b and not equal_a:
278 yield 'a', ia, amatch
284 yield 'a', ia, amatch
279 elif not equal_a and not equal_b:
285 elif not equal_a and not equal_b:
280 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
286 yield 'conflict', iz, zmatch, ia, amatch, ib, bmatch
281 else:
287 else:
282 raise AssertionError("can't handle a=b=base but unmatched")
288 raise AssertionError("can't handle a=b=base but unmatched")
283
289
284 ia = amatch
290 ia = amatch
285 ib = bmatch
291 ib = bmatch
286 iz = zmatch
292 iz = zmatch
287
293
288 # if the same part of the base was deleted on both sides
294 # if the same part of the base was deleted on both sides
289 # that's OK, we can just skip it.
295 # that's OK, we can just skip it.
290
296
291
297
292 if matchlen > 0:
298 if matchlen > 0:
293 assert ia == amatch
299 assert ia == amatch
294 assert ib == bmatch
300 assert ib == bmatch
295 assert iz == zmatch
301 assert iz == zmatch
296
302
297 yield 'unchanged', zmatch, zend
303 yield 'unchanged', zmatch, zend
298 iz = zend
304 iz = zend
299 ia = aend
305 ia = aend
300 ib = bend
306 ib = bend
301
307
302
308
303 def reprocess_merge_regions(self, merge_regions):
309 def reprocess_merge_regions(self, merge_regions):
304 """Where there are conflict regions, remove the agreed lines.
310 """Where there are conflict regions, remove the agreed lines.
305
311
306 Lines where both A and B have made the same changes are
312 Lines where both A and B have made the same changes are
307 eliminated.
313 eliminated.
308 """
314 """
309 for region in merge_regions:
315 for region in merge_regions:
310 if region[0] != "conflict":
316 if region[0] != "conflict":
311 yield region
317 yield region
312 continue
318 continue
313 type, iz, zmatch, ia, amatch, ib, bmatch = region
319 type, iz, zmatch, ia, amatch, ib, bmatch = region
314 a_region = self.a[ia:amatch]
320 a_region = self.a[ia:amatch]
315 b_region = self.b[ib:bmatch]
321 b_region = self.b[ib:bmatch]
316 matches = mdiff.get_matching_blocks(''.join(a_region),
322 matches = mdiff.get_matching_blocks(''.join(a_region),
317 ''.join(b_region))
323 ''.join(b_region))
318 next_a = ia
324 next_a = ia
319 next_b = ib
325 next_b = ib
320 for region_ia, region_ib, region_len in matches[:-1]:
326 for region_ia, region_ib, region_len in matches[:-1]:
321 region_ia += ia
327 region_ia += ia
322 region_ib += ib
328 region_ib += ib
323 reg = self.mismatch_region(next_a, region_ia, next_b,
329 reg = self.mismatch_region(next_a, region_ia, next_b,
324 region_ib)
330 region_ib)
325 if reg is not None:
331 if reg is not None:
326 yield reg
332 yield reg
327 yield 'same', region_ia, region_len+region_ia
333 yield 'same', region_ia, region_len+region_ia
328 next_a = region_ia + region_len
334 next_a = region_ia + region_len
329 next_b = region_ib + region_len
335 next_b = region_ib + region_len
330 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
336 reg = self.mismatch_region(next_a, amatch, next_b, bmatch)
331 if reg is not None:
337 if reg is not None:
332 yield reg
338 yield reg
333
339
334
340
335 def mismatch_region(next_a, region_ia, next_b, region_ib):
341 def mismatch_region(next_a, region_ia, next_b, region_ib):
336 if next_a < region_ia or next_b < region_ib:
342 if next_a < region_ia or next_b < region_ib:
337 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
343 return 'conflict', None, None, next_a, region_ia, next_b, region_ib
338 mismatch_region = staticmethod(mismatch_region)
344 mismatch_region = staticmethod(mismatch_region)
339
345
340
346
341 def find_sync_regions(self):
347 def find_sync_regions(self):
342 """Return a list of sync regions, where both descendents match the base.
348 """Return a list of sync regions, where both descendents match the base.
343
349
344 Generates a list of (base1, base2, a1, a2, b1, b2). There is
350 Generates a list of (base1, base2, a1, a2, b1, b2). There is
345 always a zero-length sync region at the end of all the files.
351 always a zero-length sync region at the end of all the files.
346 """
352 """
347
353
348 ia = ib = 0
354 ia = ib = 0
349 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
355 amatches = mdiff.get_matching_blocks(self.basetext, self.atext)
350 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
356 bmatches = mdiff.get_matching_blocks(self.basetext, self.btext)
351 len_a = len(amatches)
357 len_a = len(amatches)
352 len_b = len(bmatches)
358 len_b = len(bmatches)
353
359
354 sl = []
360 sl = []
355
361
356 while ia < len_a and ib < len_b:
362 while ia < len_a and ib < len_b:
357 abase, amatch, alen = amatches[ia]
363 abase, amatch, alen = amatches[ia]
358 bbase, bmatch, blen = bmatches[ib]
364 bbase, bmatch, blen = bmatches[ib]
359
365
360 # there is an unconflicted block at i; how long does it
366 # there is an unconflicted block at i; how long does it
361 # extend? until whichever one ends earlier.
367 # extend? until whichever one ends earlier.
362 i = intersect((abase, abase+alen), (bbase, bbase+blen))
368 i = intersect((abase, abase+alen), (bbase, bbase+blen))
363 if i:
369 if i:
364 intbase = i[0]
370 intbase = i[0]
365 intend = i[1]
371 intend = i[1]
366 intlen = intend - intbase
372 intlen = intend - intbase
367
373
368 # found a match of base[i[0], i[1]]; this may be less than
374 # found a match of base[i[0], i[1]]; this may be less than
369 # the region that matches in either one
375 # the region that matches in either one
370 assert intlen <= alen
376 assert intlen <= alen
371 assert intlen <= blen
377 assert intlen <= blen
372 assert abase <= intbase
378 assert abase <= intbase
373 assert bbase <= intbase
379 assert bbase <= intbase
374
380
375 asub = amatch + (intbase - abase)
381 asub = amatch + (intbase - abase)
376 bsub = bmatch + (intbase - bbase)
382 bsub = bmatch + (intbase - bbase)
377 aend = asub + intlen
383 aend = asub + intlen
378 bend = bsub + intlen
384 bend = bsub + intlen
379
385
380 assert self.base[intbase:intend] == self.a[asub:aend], \
386 assert self.base[intbase:intend] == self.a[asub:aend], \
381 (self.base[intbase:intend], self.a[asub:aend])
387 (self.base[intbase:intend], self.a[asub:aend])
382
388
383 assert self.base[intbase:intend] == self.b[bsub:bend]
389 assert self.base[intbase:intend] == self.b[bsub:bend]
384
390
385 sl.append((intbase, intend,
391 sl.append((intbase, intend,
386 asub, aend,
392 asub, aend,
387 bsub, bend))
393 bsub, bend))
388
394
389 # advance whichever one ends first in the base text
395 # advance whichever one ends first in the base text
390 if (abase + alen) < (bbase + blen):
396 if (abase + alen) < (bbase + blen):
391 ia += 1
397 ia += 1
392 else:
398 else:
393 ib += 1
399 ib += 1
394
400
395 intbase = len(self.base)
401 intbase = len(self.base)
396 abase = len(self.a)
402 abase = len(self.a)
397 bbase = len(self.b)
403 bbase = len(self.b)
398 sl.append((intbase, intbase, abase, abase, bbase, bbase))
404 sl.append((intbase, intbase, abase, abase, bbase, bbase))
399
405
400 return sl
406 return sl
401
407
402
408
403
409
404 def find_unconflicted(self):
410 def find_unconflicted(self):
405 """Return a list of ranges in base that are not conflicted."""
411 """Return a list of ranges in base that are not conflicted."""
406 am = mdiff.get_matching_blocks(self.basetext, self.atext)
412 am = mdiff.get_matching_blocks(self.basetext, self.atext)
407 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
413 bm = mdiff.get_matching_blocks(self.basetext, self.btext)
408
414
409 unc = []
415 unc = []
410
416
411 while am and bm:
417 while am and bm:
412 # there is an unconflicted block at i; how long does it
418 # there is an unconflicted block at i; how long does it
413 # extend? until whichever one ends earlier.
419 # extend? until whichever one ends earlier.
414 a1 = am[0][0]
420 a1 = am[0][0]
415 a2 = a1 + am[0][2]
421 a2 = a1 + am[0][2]
416 b1 = bm[0][0]
422 b1 = bm[0][0]
417 b2 = b1 + bm[0][2]
423 b2 = b1 + bm[0][2]
418 i = intersect((a1, a2), (b1, b2))
424 i = intersect((a1, a2), (b1, b2))
419 if i:
425 if i:
420 unc.append(i)
426 unc.append(i)
421
427
422 if a2 < b2:
428 if a2 < b2:
423 del am[0]
429 del am[0]
424 else:
430 else:
425 del bm[0]
431 del bm[0]
426
432
427 return unc
433 return unc
428
434
429
435
430 # bzr compatible interface, for the tests
436 # bzr compatible interface, for the tests
431 class Merge3(Merge3Text):
437 class Merge3(Merge3Text):
432 """3-way merge of texts.
438 """3-way merge of texts.
433
439
434 Given BASE, OTHER, THIS, tries to produce a combined text
440 Given BASE, OTHER, THIS, tries to produce a combined text
435 incorporating the changes from both BASE->OTHER and BASE->THIS.
441 incorporating the changes from both BASE->OTHER and BASE->THIS.
436 All three will typically be sequences of lines."""
442 All three will typically be sequences of lines."""
437 def __init__(self, base, a, b):
443 def __init__(self, base, a, b):
438 basetext = '\n'.join([i.strip('\n') for i in base] + [''])
444 basetext = '\n'.join([i.strip('\n') for i in base] + [''])
439 atext = '\n'.join([i.strip('\n') for i in a] + [''])
445 atext = '\n'.join([i.strip('\n') for i in a] + [''])
440 btext = '\n'.join([i.strip('\n') for i in b] + [''])
446 btext = '\n'.join([i.strip('\n') for i in b] + [''])
441 if util.binary(basetext) or util.binary(atext) or util.binary(btext):
447 if util.binary(basetext) or util.binary(atext) or util.binary(btext):
442 raise util.Abort(_("don't know how to merge binary files"))
448 raise util.Abort(_("don't know how to merge binary files"))
443 Merge3Text.__init__(self, basetext, atext, btext, base, a, b)
449 Merge3Text.__init__(self, basetext, atext, btext, base, a, b)
444
450
445
451
446 def simplemerge(local, base, other, **opts):
452 def simplemerge(local, base, other, **opts):
447 def readfile(filename):
453 def readfile(filename):
448 f = open(filename, "rb")
454 f = open(filename, "rb")
449 text = f.read()
455 text = f.read()
450 f.close()
456 f.close()
451 if util.binary(text):
457 if util.binary(text):
452 msg = _("%s looks like a binary file.") % filename
458 msg = _("%s looks like a binary file.") % filename
453 if not opts.get('text'):
459 if not opts.get('text'):
454 raise util.Abort(msg)
460 raise util.Abort(msg)
455 elif not opts.get('quiet'):
461 elif not opts.get('quiet'):
456 sys.stderr.write(_('warning: %s\n') % msg)
462 warn(_('warning: %s\n') % msg)
457 return text
463 return text
458
464
459 name_a = local
465 name_a = local
460 name_b = other
466 name_b = other
461 labels = opts.get('label', [])
467 labels = opts.get('label', [])
462 if labels:
468 if labels:
463 name_a = labels.pop(0)
469 name_a = labels.pop(0)
464 if labels:
470 if labels:
465 name_b = labels.pop(0)
471 name_b = labels.pop(0)
466 if labels:
472 if labels:
467 raise util.Abort(_("can only specify two labels."))
473 raise util.Abort(_("can only specify two labels."))
468
474
469 localtext = readfile(local)
475 localtext = readfile(local)
470 basetext = readfile(base)
476 basetext = readfile(base)
471 othertext = readfile(other)
477 othertext = readfile(other)
472
478
473 orig = local
479 orig = local
474 local = os.path.realpath(local)
480 local = os.path.realpath(local)
475 if not opts.get('print'):
481 if not opts.get('print'):
476 opener = util.opener(os.path.dirname(local))
482 opener = util.opener(os.path.dirname(local))
477 out = opener(os.path.basename(local), "w", atomictemp=True)
483 out = opener(os.path.basename(local), "w", atomictemp=True)
478 else:
484 else:
479 out = sys.stdout
485 out = sys.stdout
480
486
481 reprocess = not opts.get('no_minimal')
487 reprocess = not opts.get('no_minimal')
482
488
483 m3 = Merge3Text(basetext, localtext, othertext)
489 m3 = Merge3Text(basetext, localtext, othertext)
484 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
490 for line in m3.merge_lines(name_a=name_a, name_b=name_b,
485 reprocess=reprocess):
491 reprocess=reprocess):
486 out.write(line)
492 out.write(line)
487
493
488 if not opts.get('print'):
494 if not opts.get('print'):
489 out.rename()
495 out.rename()
490
496
491 if m3.conflicts:
497 if m3.conflicts:
492 if not opts.get('quiet'):
498 if not opts.get('quiet'):
493 sys.stdout.flush()
499 warn(_("warning: conflicts during merge.\n"))
494 sys.stderr.write(_("warning: conflicts during merge.\n"))
495 return 1
500 return 1
496
501
497 options = [('L', 'label', [], _('labels to use on conflict markers')),
502 options = [('L', 'label', [], _('labels to use on conflict markers')),
498 ('a', 'text', None, _('treat all files as text')),
503 ('a', 'text', None, _('treat all files as text')),
499 ('p', 'print', None,
504 ('p', 'print', None,
500 _('print results instead of overwriting LOCAL')),
505 _('print results instead of overwriting LOCAL')),
501 ('', 'no-minimal', None,
506 ('', 'no-minimal', None,
502 _('do not try to minimize conflict regions')),
507 _('do not try to minimize conflict regions')),
503 ('h', 'help', None, _('display help and exit')),
508 ('h', 'help', None, _('display help and exit')),
504 ('q', 'quiet', None, _('suppress output'))]
509 ('q', 'quiet', None, _('suppress output'))]
505
510
506 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER
511 usage = _('''simplemerge [OPTS] LOCAL BASE OTHER
507
512
508 Simple three-way file merge utility with a minimal feature set.
513 Simple three-way file merge utility with a minimal feature set.
509
514
510 Apply to LOCAL the changes necessary to go from BASE to OTHER.
515 Apply to LOCAL the changes necessary to go from BASE to OTHER.
511
516
512 By default, LOCAL is overwritten with the results of this operation.
517 By default, LOCAL is overwritten with the results of this operation.
513 ''')
518 ''')
514
519
515 def showhelp():
520 def showhelp():
516 sys.stdout.write(usage)
521 sys.stdout.write(usage)
517 sys.stdout.write('\noptions:\n')
522 sys.stdout.write('\noptions:\n')
518
523
519 out_opts = []
524 out_opts = []
520 for shortopt, longopt, default, desc in options:
525 for shortopt, longopt, default, desc in options:
521 out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt,
526 out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt,
522 longopt and ' --%s' % longopt),
527 longopt and ' --%s' % longopt),
523 '%s' % desc))
528 '%s' % desc))
524 opts_len = max([len(opt[0]) for opt in out_opts])
529 opts_len = max([len(opt[0]) for opt in out_opts])
525 for first, second in out_opts:
530 for first, second in out_opts:
526 sys.stdout.write(' %-*s %s\n' % (opts_len, first, second))
531 sys.stdout.write(' %-*s %s\n' % (opts_len, first, second))
527
532
528 class ParseError(Exception):
533 class ParseError(Exception):
529 """Exception raised on errors in parsing the command line."""
534 """Exception raised on errors in parsing the command line."""
530
535
531 def main(argv):
536 def main(argv):
532 try:
537 try:
533 opts = {}
538 opts = {}
534 try:
539 try:
535 args = fancyopts.fancyopts(argv[1:], options, opts)
540 args = fancyopts.fancyopts(argv[1:], options, opts)
536 except fancyopts.getopt.GetoptError, e:
541 except fancyopts.getopt.GetoptError, e:
537 raise ParseError(e)
542 raise ParseError(e)
538 if opts['help']:
543 if opts['help']:
539 showhelp()
544 showhelp()
540 return 0
545 return 0
541 if len(args) != 3:
546 if len(args) != 3:
542 raise ParseError(_('wrong number of arguments'))
547 raise ParseError(_('wrong number of arguments'))
543 return simplemerge(*args, **opts)
548 return simplemerge(*args, **opts)
544 except ParseError, e:
549 except ParseError, e:
545 sys.stdout.write("%s: %s\n" % (sys.argv[0], e))
550 sys.stdout.write("%s: %s\n" % (sys.argv[0], e))
546 showhelp()
551 showhelp()
547 return 1
552 return 1
548 except util.Abort, e:
553 except util.Abort, e:
549 sys.stderr.write("abort: %s\n" % e)
554 sys.stderr.write("abort: %s\n" % e)
550 return 255
555 return 255
551 except KeyboardInterrupt:
556 except KeyboardInterrupt:
552 return 255
557 return 255
553
558
554 if __name__ == '__main__':
559 if __name__ == '__main__':
555 import sys
560 import sys
556 import os
561 import os
557 sys.exit(main(sys.argv))
562 sys.exit(main(sys.argv))
General Comments 0
You need to be logged in to leave comments. Login now