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