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