~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Aaron Bentley
  • Date: 2009-04-03 20:05:25 UTC
  • mto: This revision was merged to the branch mainline in revision 4266.
  • Revision ID: aaron@aaronbentley.com-20090403200525-5vcdyhnjrlsqd6dr
Support hidden options.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# Authors:
4
4
#   Johan Rydberg <jrydberg@gnu.org>
30
30
import urllib
31
31
 
32
32
from bzrlib import (
33
 
    annotate,
34
33
    errors,
35
 
    graph as _mod_graph,
36
 
    groupcompress,
37
34
    index,
38
35
    knit,
39
36
    osutils,
42
39
    revision,
43
40
    ui,
44
41
    )
45
 
from bzrlib.graph import DictParentsProvider, Graph, StackedParentsProvider
 
42
from bzrlib.graph import DictParentsProvider, Graph, _StackedParentsProvider
46
43
from bzrlib.transport.memory import MemoryTransport
47
44
""")
 
45
from bzrlib.inter import InterObject
48
46
from bzrlib.registry import Registry
49
47
from bzrlib.symbol_versioning import *
50
48
from bzrlib.textmerge import TextMerge
51
 
from bzrlib import bencode
 
49
from bzrlib.util import bencode
52
50
 
53
51
 
54
52
adapter_registry = Registry()
175
173
        self.key = key
176
174
        self.parents = None
177
175
 
178
 
    def get_bytes_as(self, storage_kind):
179
 
        raise ValueError('A request was made for key: %s, but that'
180
 
                         ' content is not available, and the calling'
181
 
                         ' code does not handle if it is missing.'
182
 
                         % (self.key,))
183
 
 
184
176
 
185
177
class AdapterFactory(ContentFactory):
186
178
    """A content factory to adapt between key prefix's."""
836
828
        """
837
829
        raise NotImplementedError(self.add_lines)
838
830
 
839
 
    def _add_text(self, key, parents, text, nostore_sha=None, random_id=False):
840
 
        """Add a text to the store.
841
 
 
842
 
        This is a private function for use by CommitBuilder.
843
 
 
844
 
        :param key: The key tuple of the text to add. If the last element is
845
 
            None, a CHK string will be generated during the addition.
846
 
        :param parents: The parents key tuples of the text to add.
847
 
        :param text: A string containing the text to be committed.
848
 
        :param nostore_sha: Raise ExistingContent and do not add the lines to
849
 
            the versioned file if the digest of the lines matches this.
850
 
        :param random_id: If True a random id has been selected rather than
851
 
            an id determined by some deterministic process such as a converter
852
 
            from a foreign VCS. When True the backend may choose not to check
853
 
            for uniqueness of the resulting key within the versioned file, so
854
 
            this should only be done when the result is expected to be unique
855
 
            anyway.
856
 
        :param check_content: If True, the lines supplied are verified to be
857
 
            bytestrings that are correctly formed lines.
858
 
        :return: The text sha1, the number of bytes in the text, and an opaque
859
 
                 representation of the inserted version which can be provided
860
 
                 back to future _add_text calls in the parent_texts dictionary.
861
 
        """
862
 
        # The default implementation just thunks over to .add_lines(),
863
 
        # inefficient, but it works.
864
 
        return self.add_lines(key, parents, osutils.split_lines(text),
865
 
                              nostore_sha=nostore_sha,
866
 
                              random_id=random_id,
867
 
                              check_content=True)
868
 
 
869
831
    def add_mpdiffs(self, records):
870
832
        """Add mpdiffs to this VersionedFile.
871
833
 
913
875
        raise NotImplementedError(self.annotate)
914
876
 
915
877
    def check(self, progress_bar=None):
916
 
        """Check this object for integrity.
917
 
        
918
 
        :param progress_bar: A progress bar to output as the check progresses.
919
 
        :param keys: Specific keys within the VersionedFiles to check. When
920
 
            this parameter is not None, check() becomes a generator as per
921
 
            get_record_stream. The difference to get_record_stream is that
922
 
            more or deeper checks will be performed.
923
 
        :return: None, or if keys was supplied a generator as per
924
 
            get_record_stream.
925
 
        """
 
878
        """Check this object for integrity."""
926
879
        raise NotImplementedError(self.check)
927
880
 
928
881
    @staticmethod
929
882
    def check_not_reserved_id(version_id):
930
883
        revision.check_not_reserved_id(version_id)
931
884
 
932
 
    def clear_cache(self):
933
 
        """Clear whatever caches this VersionedFile holds.
934
 
 
935
 
        This is generally called after an operation has been performed, when we
936
 
        don't expect to be using this versioned file again soon.
937
 
        """
938
 
 
939
885
    def _check_lines_not_unicode(self, lines):
940
886
        """Check that lines being added to a versioned file are not unicode."""
941
887
        for line in lines:
948
894
            if '\n' in line[:-1]:
949
895
                raise errors.BzrBadParameterContainsNewline("lines")
950
896
 
951
 
    def get_known_graph_ancestry(self, keys):
952
 
        """Get a KnownGraph instance with the ancestry of keys."""
953
 
        # most basic implementation is a loop around get_parent_map
954
 
        pending = set(keys)
955
 
        parent_map = {}
956
 
        while pending:
957
 
            this_parent_map = self.get_parent_map(pending)
958
 
            parent_map.update(this_parent_map)
959
 
            pending = set()
960
 
            map(pending.update, this_parent_map.itervalues())
961
 
            pending = pending.difference(parent_map)
962
 
        kg = _mod_graph.KnownGraph(parent_map)
963
 
        return kg
964
 
 
965
897
    def get_parent_map(self, keys):
966
898
        """Get a map of the parents of keys.
967
899
 
1159
1091
            result.append((prefix + (origin,), line))
1160
1092
        return result
1161
1093
 
1162
 
    def get_annotator(self):
1163
 
        return annotate.Annotator(self)
1164
 
 
1165
 
    def check(self, progress_bar=None, keys=None):
 
1094
    def check(self, progress_bar=None):
1166
1095
        """See VersionedFiles.check()."""
1167
 
        # XXX: This is over-enthusiastic but as we only thunk for Weaves today
1168
 
        # this is tolerable. Ideally we'd pass keys down to check() and 
1169
 
        # have the older VersiondFile interface updated too.
1170
1096
        for prefix, vf in self._iter_all_components():
1171
1097
            vf.check()
1172
 
        if keys is not None:
1173
 
            return self.get_record_stream(keys, 'unordered', True)
1174
1098
 
1175
1099
    def get_parent_map(self, keys):
1176
1100
        """Get a map of the parents of keys.
1408
1332
            result[revision.NULL_REVISION] = ()
1409
1333
        self._providers = self._providers[:1] + self.fallback_versionedfiles
1410
1334
        result.update(
1411
 
            StackedParentsProvider(self._providers).get_parent_map(keys))
 
1335
            _StackedParentsProvider(self._providers).get_parent_map(keys))
1412
1336
        for key, parents in result.iteritems():
1413
1337
            if parents == ():
1414
1338
                result[key] = (revision.NULL_REVISION,)
1425
1349
    def __init__(self, plan, a_marker=TextMerge.A_MARKER,
1426
1350
                 b_marker=TextMerge.B_MARKER):
1427
1351
        TextMerge.__init__(self, a_marker, b_marker)
1428
 
        self.plan = list(plan)
 
1352
        self.plan = plan
1429
1353
 
1430
1354
    def _merge_struct(self):
1431
1355
        lines_a = []
1478
1402
            elif state == 'conflicted-b':
1479
1403
                ch_b = ch_a = True
1480
1404
                lines_b.append(line)
1481
 
            elif state == 'killed-both':
1482
 
                # This counts as a change, even though there is no associated
1483
 
                # line
1484
 
                ch_b = ch_a = True
1485
1405
            else:
1486
1406
                if state not in ('irrelevant', 'ghost-a', 'ghost-b',
1487
 
                        'killed-base'):
 
1407
                        'killed-base', 'killed-both'):
1488
1408
                    raise AssertionError(state)
1489
1409
        for struct in outstanding_struct():
1490
1410
            yield struct
1491
1411
 
1492
 
    def base_from_plan(self):
1493
 
        """Construct a BASE file from the plan text."""
1494
 
        base_lines = []
1495
 
        for state, line in self.plan:
1496
 
            if state in ('killed-a', 'killed-b', 'killed-both', 'unchanged'):
1497
 
                # If unchanged, then this line is straight from base. If a or b
1498
 
                # or both killed the line, then it *used* to be in base.
1499
 
                base_lines.append(line)
1500
 
            else:
1501
 
                if state not in ('killed-base', 'irrelevant',
1502
 
                                 'ghost-a', 'ghost-b',
1503
 
                                 'new-a', 'new-b',
1504
 
                                 'conflicted-a', 'conflicted-b'):
1505
 
                    # killed-base, irrelevant means it doesn't apply
1506
 
                    # ghost-a/ghost-b are harder to say for sure, but they
1507
 
                    # aren't in the 'inc_c' which means they aren't in the
1508
 
                    # shared base of a & b. So we don't include them.  And
1509
 
                    # obviously if the line is newly inserted, it isn't in base
1510
 
 
1511
 
                    # If 'conflicted-a' or b, then it is new vs one base, but
1512
 
                    # old versus another base. However, if we make it present
1513
 
                    # in the base, it will be deleted from the target, and it
1514
 
                    # seems better to get a line doubled in the merge result,
1515
 
                    # rather than have it deleted entirely.
1516
 
                    # Example, each node is the 'text' at that point:
1517
 
                    #           MN
1518
 
                    #          /   \
1519
 
                    #        MaN   MbN
1520
 
                    #         |  X  |
1521
 
                    #        MabN MbaN
1522
 
                    #          \   /
1523
 
                    #           ???
1524
 
                    # There was a criss-cross conflict merge. Both sides
1525
 
                    # include the other, but put themselves first.
1526
 
                    # Weave marks this as a 'clean' merge, picking OTHER over
1527
 
                    # THIS. (Though the details depend on order inserted into
1528
 
                    # weave, etc.)
1529
 
                    # LCA generates a plan:
1530
 
                    # [('unchanged', M),
1531
 
                    #  ('conflicted-b', b),
1532
 
                    #  ('unchanged', a),
1533
 
                    #  ('conflicted-a', b),
1534
 
                    #  ('unchanged', N)]
1535
 
                    # If you mark 'conflicted-*' as part of BASE, then a 3-way
1536
 
                    # merge tool will cleanly generate "MaN" (as BASE vs THIS
1537
 
                    # removes one 'b', and BASE vs OTHER removes the other)
1538
 
                    # If you include neither, 3-way creates a clean "MbabN" as
1539
 
                    # THIS adds one 'b', and OTHER does too.
1540
 
                    # It seems that having the line 2 times is better than
1541
 
                    # having it omitted. (Easier to manually delete than notice
1542
 
                    # it needs to be added.)
1543
 
                    raise AssertionError('Unknown state: %s' % (state,))
1544
 
        return base_lines
1545
 
 
1546
1412
 
1547
1413
class WeaveMerge(PlanWeaveMerge):
1548
1414
    """Weave merge that takes a VersionedFile and two versions as its input."""
1646
1512
            record.get_bytes_as(record.storage_kind) call.
1647
1513
        """
1648
1514
        self._bytes_iterator = bytes_iterator
1649
 
        self._kind_factory = {
1650
 
            'fulltext': fulltext_network_to_record,
1651
 
            'groupcompress-block': groupcompress.network_block_to_records,
1652
 
            'knit-ft-gz': knit.knit_network_to_record,
1653
 
            'knit-delta-gz': knit.knit_network_to_record,
1654
 
            'knit-annotated-ft-gz': knit.knit_network_to_record,
1655
 
            'knit-annotated-delta-gz': knit.knit_network_to_record,
1656
 
            'knit-delta-closure': knit.knit_delta_closure_to_records,
 
1515
        self._kind_factory = {'knit-ft-gz':knit.knit_network_to_record,
 
1516
            'knit-delta-gz':knit.knit_network_to_record,
 
1517
            'knit-annotated-ft-gz':knit.knit_network_to_record,
 
1518
            'knit-annotated-delta-gz':knit.knit_network_to_record,
 
1519
            'knit-delta-closure':knit.knit_delta_closure_to_records,
 
1520
            'fulltext':fulltext_network_to_record,
1657
1521
            }
1658
1522
 
1659
1523
    def read(self):