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