~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Ian Clatworthy
  • Date: 2010-02-19 03:02:07 UTC
  • mto: (4797.23.1 integration-2.1)
  • mto: This revision was merged to the branch mainline in revision 5055.
  • Revision ID: ian.clatworthy@canonical.com-20100219030207-zpbzx021zavx4sqt
What's New in 2.1 - a summary of changes since 2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# Authors:
4
4
#   Johan Rydberg <jrydberg@gnu.org>
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,
913
914
        raise NotImplementedError(self.annotate)
914
915
 
915
916
    def check(self, progress_bar=None):
916
 
        """Check this object for integrity."""
 
917
        """Check this object for integrity.
 
918
        
 
919
        :param progress_bar: A progress bar to output as the check progresses.
 
920
        :param keys: Specific keys within the VersionedFiles to check. When
 
921
            this parameter is not None, check() becomes a generator as per
 
922
            get_record_stream. The difference to get_record_stream is that
 
923
            more or deeper checks will be performed.
 
924
        :return: None, or if keys was supplied a generator as per
 
925
            get_record_stream.
 
926
        """
917
927
        raise NotImplementedError(self.check)
918
928
 
919
929
    @staticmethod
920
930
    def check_not_reserved_id(version_id):
921
931
        revision.check_not_reserved_id(version_id)
922
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
 
923
940
    def _check_lines_not_unicode(self, lines):
924
941
        """Check that lines being added to a versioned file are not unicode."""
925
942
        for line in lines:
932
949
            if '\n' in line[:-1]:
933
950
                raise errors.BzrBadParameterContainsNewline("lines")
934
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
 
935
966
    def get_parent_map(self, keys):
936
967
        """Get a map of the parents of keys.
937
968
 
1132
1163
    def get_annotator(self):
1133
1164
        return annotate.Annotator(self)
1134
1165
 
1135
 
    def check(self, progress_bar=None):
 
1166
    def check(self, progress_bar=None, keys=None):
1136
1167
        """See VersionedFiles.check()."""
 
1168
        # XXX: This is over-enthusiastic but as we only thunk for Weaves today
 
1169
        # this is tolerable. Ideally we'd pass keys down to check() and 
 
1170
        # have the older VersiondFile interface updated too.
1137
1171
        for prefix, vf in self._iter_all_components():
1138
1172
            vf.check()
 
1173
        if keys is not None:
 
1174
            return self.get_record_stream(keys, 'unordered', True)
1139
1175
 
1140
1176
    def get_parent_map(self, keys):
1141
1177
        """Get a map of the parents of keys.
1390
1426
    def __init__(self, plan, a_marker=TextMerge.A_MARKER,
1391
1427
                 b_marker=TextMerge.B_MARKER):
1392
1428
        TextMerge.__init__(self, a_marker, b_marker)
1393
 
        self.plan = plan
 
1429
        self.plan = list(plan)
1394
1430
 
1395
1431
    def _merge_struct(self):
1396
1432
        lines_a = []
1454
1490
        for struct in outstanding_struct():
1455
1491
            yield struct
1456
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
 
1457
1547
 
1458
1548
class WeaveMerge(PlanWeaveMerge):
1459
1549
    """Weave merge that takes a VersionedFile and two versions as its input."""
1557
1647
            record.get_bytes_as(record.storage_kind) call.
1558
1648
        """
1559
1649
        self._bytes_iterator = bytes_iterator
1560
 
        self._kind_factory = {'knit-ft-gz':knit.knit_network_to_record,
1561
 
            'knit-delta-gz':knit.knit_network_to_record,
1562
 
            'knit-annotated-ft-gz':knit.knit_network_to_record,
1563
 
            'knit-annotated-delta-gz':knit.knit_network_to_record,
1564
 
            'knit-delta-closure':knit.knit_delta_closure_to_records,
1565
 
            'fulltext':fulltext_network_to_record,
1566
 
            '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,
1567
1658
            }
1568
1659
 
1569
1660
    def read(self):