~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Martin Pool
  • Date: 2010-01-29 14:09:05 UTC
  • mto: This revision was merged to the branch mainline in revision 4992.
  • Revision ID: mbp@sourcefrog.net-20100129140905-2uiarb6p8di1ywsr
Correction to url

from review: https://code.edge.launchpad.net/~mbp/bzr/doc/+merge/18250

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
from bzrlib import (
33
33
    annotate,
34
34
    errors,
 
35
    graph as _mod_graph,
35
36
    groupcompress,
36
37
    index,
37
38
    knit,
929
930
    def check_not_reserved_id(version_id):
930
931
        revision.check_not_reserved_id(version_id)
931
932
 
 
933
    def clear_cache(self):
 
934
        """Clear whatever caches this VersionedFile holds.
 
935
 
 
936
        This is generally called after an operation has been performed, when we
 
937
        don't expect to be using this versioned file again soon.
 
938
        """
 
939
 
932
940
    def _check_lines_not_unicode(self, lines):
933
941
        """Check that lines being added to a versioned file are not unicode."""
934
942
        for line in lines:
941
949
            if '\n' in line[:-1]:
942
950
                raise errors.BzrBadParameterContainsNewline("lines")
943
951
 
 
952
    def get_known_graph_ancestry(self, keys):
 
953
        """Get a KnownGraph instance with the ancestry of keys."""
 
954
        # most basic implementation is a loop around get_parent_map
 
955
        pending = set(keys)
 
956
        parent_map = {}
 
957
        while pending:
 
958
            this_parent_map = self.get_parent_map(pending)
 
959
            parent_map.update(this_parent_map)
 
960
            pending = set()
 
961
            map(pending.update, this_parent_map.itervalues())
 
962
            pending = pending.difference(parent_map)
 
963
        kg = _mod_graph.KnownGraph(parent_map)
 
964
        return kg
 
965
 
944
966
    def get_parent_map(self, keys):
945
967
        """Get a map of the parents of keys.
946
968
 
1404
1426
    def __init__(self, plan, a_marker=TextMerge.A_MARKER,
1405
1427
                 b_marker=TextMerge.B_MARKER):
1406
1428
        TextMerge.__init__(self, a_marker, b_marker)
1407
 
        self.plan = plan
 
1429
        self.plan = list(plan)
1408
1430
 
1409
1431
    def _merge_struct(self):
1410
1432
        lines_a = []
1468
1490
        for struct in outstanding_struct():
1469
1491
            yield struct
1470
1492
 
 
1493
    def base_from_plan(self):
 
1494
        """Construct a BASE file from the plan text."""
 
1495
        base_lines = []
 
1496
        for state, line in self.plan:
 
1497
            if state in ('killed-a', 'killed-b', 'killed-both', 'unchanged'):
 
1498
                # If unchanged, then this line is straight from base. If a or b
 
1499
                # or both killed the line, then it *used* to be in base.
 
1500
                base_lines.append(line)
 
1501
            else:
 
1502
                if state not in ('killed-base', 'irrelevant',
 
1503
                                 'ghost-a', 'ghost-b',
 
1504
                                 'new-a', 'new-b',
 
1505
                                 'conflicted-a', 'conflicted-b'):
 
1506
                    # killed-base, irrelevant means it doesn't apply
 
1507
                    # ghost-a/ghost-b are harder to say for sure, but they
 
1508
                    # aren't in the 'inc_c' which means they aren't in the
 
1509
                    # shared base of a & b. So we don't include them.  And
 
1510
                    # obviously if the line is newly inserted, it isn't in base
 
1511
 
 
1512
                    # If 'conflicted-a' or b, then it is new vs one base, but
 
1513
                    # old versus another base. However, if we make it present
 
1514
                    # in the base, it will be deleted from the target, and it
 
1515
                    # seems better to get a line doubled in the merge result,
 
1516
                    # rather than have it deleted entirely.
 
1517
                    # Example, each node is the 'text' at that point:
 
1518
                    #           MN
 
1519
                    #          /   \
 
1520
                    #        MaN   MbN
 
1521
                    #         |  X  |
 
1522
                    #        MabN MbaN
 
1523
                    #          \   /
 
1524
                    #           ???
 
1525
                    # There was a criss-cross conflict merge. Both sides
 
1526
                    # include the other, but put themselves first.
 
1527
                    # Weave marks this as a 'clean' merge, picking OTHER over
 
1528
                    # THIS. (Though the details depend on order inserted into
 
1529
                    # weave, etc.)
 
1530
                    # LCA generates a plan:
 
1531
                    # [('unchanged', M),
 
1532
                    #  ('conflicted-b', b),
 
1533
                    #  ('unchanged', a),
 
1534
                    #  ('conflicted-a', b),
 
1535
                    #  ('unchanged', N)]
 
1536
                    # If you mark 'conflicted-*' as part of BASE, then a 3-way
 
1537
                    # merge tool will cleanly generate "MaN" (as BASE vs THIS
 
1538
                    # removes one 'b', and BASE vs OTHER removes the other)
 
1539
                    # If you include neither, 3-way creates a clean "MbabN" as
 
1540
                    # THIS adds one 'b', and OTHER does too.
 
1541
                    # It seems that having the line 2 times is better than
 
1542
                    # having it omitted. (Easier to manually delete than notice
 
1543
                    # it needs to be added.)
 
1544
                    raise AssertionError('Unknown state: %s' % (state,))
 
1545
        return base_lines
 
1546
 
1471
1547
 
1472
1548
class WeaveMerge(PlanWeaveMerge):
1473
1549
    """Weave merge that takes a VersionedFile and two versions as its input."""
1571
1647
            record.get_bytes_as(record.storage_kind) call.
1572
1648
        """
1573
1649
        self._bytes_iterator = bytes_iterator
1574
 
        self._kind_factory = {'knit-ft-gz':knit.knit_network_to_record,
1575
 
            'knit-delta-gz':knit.knit_network_to_record,
1576
 
            'knit-annotated-ft-gz':knit.knit_network_to_record,
1577
 
            'knit-annotated-delta-gz':knit.knit_network_to_record,
1578
 
            'knit-delta-closure':knit.knit_delta_closure_to_records,
1579
 
            'fulltext':fulltext_network_to_record,
1580
 
            'groupcompress-block':groupcompress.network_block_to_records,
 
1650
        self._kind_factory = {
 
1651
            'fulltext': fulltext_network_to_record,
 
1652
            'groupcompress-block': groupcompress.network_block_to_records,
 
1653
            'knit-ft-gz': knit.knit_network_to_record,
 
1654
            'knit-delta-gz': knit.knit_network_to_record,
 
1655
            'knit-annotated-ft-gz': knit.knit_network_to_record,
 
1656
            'knit-annotated-delta-gz': knit.knit_network_to_record,
 
1657
            'knit-delta-closure': knit.knit_delta_closure_to_records,
1581
1658
            }
1582
1659
 
1583
1660
    def read(self):