~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-04-18 05:59:25 UTC
  • mfrom: (1551.6.16 Aaron's mergeable stuff)
  • Revision ID: pqm@pqm.ubuntu.com-20060418055925-0f7129a54583f4d8
implementation of --reprocess for weave merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
import bzrlib.errors as errors
32
32
from bzrlib.inter import InterObject
33
33
from bzrlib.symbol_versioning import *
 
34
from bzrlib.textmerge import TextMerge
34
35
from bzrlib.transport.memory import MemoryTransport
35
36
from bzrlib.tsort import topo_sort
36
37
from bzrlib import ui
394
395
        return iter(self.versions())
395
396
 
396
397
    def plan_merge(self, ver_a, ver_b):
397
 
        """Return pseudo-annotation indicating how the two versions merge.
398
 
 
399
 
        This is computed between versions a and b and their common
400
 
        base.
401
 
 
402
 
        Weave lines present in none of them are skipped entirely.
403
 
        """
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
407
 
 
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
419
 
                elif killed_a:
420
 
                    yield 'killed-a', line
421
 
                elif killed_b:
422
 
                    yield 'killed-b', line
423
 
                else:
424
 
                    yield 'unchanged', line
425
 
            elif insert in inc_a:
426
 
                if deleteset & inc_a:
427
 
                    yield 'ghost-a', line
428
 
                else:
429
 
                    # new in A; not in B
430
 
                    yield 'new-a', line
431
 
            elif insert in inc_b:
432
 
                if deleteset & inc_b:
433
 
                    yield 'ghost-b', line
434
 
                else:
435
 
                    yield 'new-b', line
436
 
            else:
437
 
                # not in either revision
438
 
                yield 'irrelevant', line
439
 
 
440
 
        yield 'unchanged', ''           # terminator
441
 
 
442
 
    def weave_merge(self, plan, a_marker='<<<<<<< \n', b_marker='>>>>>>> \n'):
 
398
        raise NotImplementedError(VersionedFile.plan_merge)
 
399
        
 
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]
 
403
 
 
404
class PlanWeaveMerge(TextMerge):
 
405
    """Weave merge that takes a plan as its input.
 
406
    
 
407
    This exists so that VersionedFile.plan_merge is implementable.
 
408
    Most callers will want to use WeaveMerge instead.
 
409
    """
 
410
 
 
411
    def __init__(self, plan, a_marker=TextMerge.A_MARKER,
 
412
                 b_marker=TextMerge.B_MARKER):
 
413
        TextMerge.__init__(self, a_marker, b_marker)
 
414
        self.plan = plan
 
415
 
 
416
 
 
417
    def _merge_struct(self):
443
418
        lines_a = []
444
419
        lines_b = []
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.
448
 
        # 
449
 
        # TODO: Show some version information (e.g. author, date) on 
450
 
        # conflicted regions.
451
 
        
 
421
       
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:
459
429
                    pass
460
430
                elif ch_a and not ch_b:
461
 
                    # one-sided change:                    
462
 
                    for l in lines_a: yield l
 
431
                    # one-sided change:
 
432
                    yield(lines_a,)
463
433
                elif ch_b and not ch_a:
464
 
                    for l in lines_b: yield l
 
434
                    yield (lines_b,)
465
435
                elif lines_a == lines_b:
466
 
                    for l in lines_a: yield l
 
436
                    yield(lines_a,)
467
437
                else:
468
 
                    yield a_marker
469
 
                    for l in lines_a: yield l
470
 
                    yield '=======\n'
471
 
                    for l in lines_b: yield l
472
 
                    yield b_marker
 
438
                    yield (lines_a, lines_b)
473
439
 
474
 
                del lines_a[:]
475
 
                del lines_b[:]
 
440
                lines_a = []
 
441
                lines_b = []
476
442
                ch_a = ch_b = False
477
443
                
478
444
            if state == 'unchanged':
479
445
                if line:
480
 
                    yield line
 
446
                    yield ([line],)
481
447
            elif state == 'killed-a':
482
448
                ch_a = True
483
449
                lines_b.append(line)
491
457
                ch_b = True
492
458
                lines_b.append(line)
493
459
            else:
494
 
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 'killed-base',
495
 
                                 'killed-both'), \
496
 
                       state
 
460
                assert state in ('irrelevant', 'ghost-a', 'ghost-b', 
 
461
                                 'killed-base', 'killed-both'), state
 
462
 
 
463
 
 
464
class WeaveMerge(PlanWeaveMerge):
 
465
    """Weave merge that takes a VersionedFile and two versions as its input"""
 
466
 
 
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)
497
471
 
498
472
 
499
473
class InterVersionedFile(InterObject):