394
395
return iter(self.versions())
396
397
def plan_merge(self, ver_a, ver_b):
397
"""Return pseudo-annotation indicating how the two versions merge.
399
This is computed between versions a and b and their common
402
Weave lines present in none of them are skipped entirely.
404
inc_a = set(self.get_ancestry([ver_a]))
405
inc_b = set(self.get_ancestry([ver_b]))
406
inc_c = inc_a & inc_b
408
for lineno, insert, deleteset, line in self.walk([ver_a, ver_b]):
409
if deleteset & inc_c:
410
# killed in parent; can't be in either a or b
411
# not relevant to our work
412
yield 'killed-base', line
413
elif insert in inc_c:
414
# was inserted in base
415
killed_a = bool(deleteset & inc_a)
416
killed_b = bool(deleteset & inc_b)
417
if killed_a and killed_b:
418
yield 'killed-both', line
420
yield 'killed-a', line
422
yield 'killed-b', line
424
yield 'unchanged', line
425
elif insert in inc_a:
426
if deleteset & inc_a:
427
yield 'ghost-a', line
431
elif insert in inc_b:
432
if deleteset & inc_b:
433
yield 'ghost-b', line
437
# not in either revision
438
yield 'irrelevant', line
440
yield 'unchanged', '' # terminator
442
def weave_merge(self, plan, a_marker='<<<<<<< \n', b_marker='>>>>>>> \n'):
398
raise NotImplementedError(VersionedFile.plan_merge)
400
def weave_merge(self, plan, a_marker=TextMerge.A_MARKER,
401
b_marker=TextMerge.B_MARKER):
402
return PlanWeaveMerge(plan, a_marker, b_marker).merge_lines()[0]
404
class PlanWeaveMerge(TextMerge):
405
"""Weave merge that takes a plan as its input.
407
This exists so that VersionedFile.plan_merge is implementable.
408
Most callers will want to use WeaveMerge instead.
411
def __init__(self, plan, a_marker=TextMerge.A_MARKER,
412
b_marker=TextMerge.B_MARKER):
413
TextMerge.__init__(self, a_marker, b_marker)
417
def _merge_struct(self):
445
420
ch_a = ch_b = False
446
# TODO: Return a structured form of the conflicts (e.g. 2-tuples for
447
# conflicted regions), rather than just inserting the markers.
449
# TODO: Show some version information (e.g. author, date) on
450
# conflicted regions.
452
422
# We previously considered either 'unchanged' or 'killed-both' lines
453
423
# to be possible places to resynchronize. However, assuming agreement
454
424
# on killed-both lines may be too agressive. -- mbp 20060324
455
for state, line in plan:
425
for state, line in self.plan:
456
426
if state == 'unchanged':
457
427
# resync and flush queued conflicts changes if any
458
428
if not lines_a and not lines_b:
460
430
elif ch_a and not ch_b:
462
for l in lines_a: yield l
463
433
elif ch_b and not ch_a:
464
for l in lines_b: yield l
465
435
elif lines_a == lines_b:
466
for l in lines_a: yield l
469
for l in lines_a: yield l
471
for l in lines_b: yield l
438
yield (lines_a, lines_b)
476
442
ch_a = ch_b = False
478
444
if state == 'unchanged':
481
447
elif state == 'killed-a':
483
449
lines_b.append(line)
492
458
lines_b.append(line)
494
assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
460
assert state in ('irrelevant', 'ghost-a', 'ghost-b',
461
'killed-base', 'killed-both'), state
464
class WeaveMerge(PlanWeaveMerge):
465
"""Weave merge that takes a VersionedFile and two versions as its input"""
467
def __init__(self, versionedfile, ver_a, ver_b,
468
a_marker=PlanWeaveMerge.A_MARKER, b_marker=PlanWeaveMerge.B_MARKER):
469
plan = versionedfile.plan_merge(ver_a, ver_b)
470
PlanWeaveMerge.__init__(self, plan, a_marker, b_marker)
499
473
class InterVersionedFile(InterObject):