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