/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/versionedfile.py

  • Committer: Andrew Bennetts
  • Date: 2009-07-15 06:39:26 UTC
  • mto: (4476.3.44 inventory-delta)
  • mto: This revision was merged to the branch mainline in revision 4608.
  • Revision ID: andrew.bennetts@canonical.com-20090715063926-9ccc4llj7bsjifdh
Fix trivial bug in xml5 error path.

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>
32
32
from bzrlib import (
33
33
    annotate,
34
34
    errors,
35
 
    graph as _mod_graph,
36
35
    groupcompress,
37
36
    index,
 
37
    inventory,
 
38
    inventory_delta,
38
39
    knit,
39
40
    osutils,
40
41
    multiparent,
45
46
from bzrlib.graph import DictParentsProvider, Graph, StackedParentsProvider
46
47
from bzrlib.transport.memory import MemoryTransport
47
48
""")
 
49
from bzrlib.inter import InterObject
48
50
from bzrlib.registry import Registry
49
51
from bzrlib.symbol_versioning import *
50
52
from bzrlib.textmerge import TextMerge
158
160
            self.storage_kind)
159
161
 
160
162
 
 
163
class InventoryDeltaContentFactory(ContentFactory):
 
164
 
 
165
    def __init__(self, key, parents, sha1, delta, basis_id, format_flags,
 
166
            repo=None):
 
167
        self.sha1 = sha1
 
168
        self.storage_kind = 'inventory-delta'
 
169
        self.key = key
 
170
        self.parents = parents
 
171
        self._delta = delta
 
172
        self._basis_id = basis_id
 
173
        self._format_flags = format_flags
 
174
        self._repo = repo
 
175
 
 
176
    def get_bytes_as(self, storage_kind):
 
177
        if storage_kind == self.storage_kind:
 
178
            return self._basis_id, self.key, self._delta, self._format_flags
 
179
        elif storage_kind == 'inventory-delta-bytes':
 
180
            serializer = inventory_delta.InventoryDeltaSerializer()
 
181
            serializer.require_flags(*self._format_flags)
 
182
            return ''.join(serializer.delta_to_lines(
 
183
                self._basis_id, self.key, self._delta))
 
184
        elif storage_kind == 'inventory-delta-bytes-from-null':
 
185
            if self._repo is None:
 
186
                raise errors.UnavailableRepresentation(self.key, storage_kind,
 
187
                    self.storage_kind)
 
188
            null_inv = inventory.Inventory(None)
 
189
            my_inv = self._repo.get_inventory(self.key) # XXX: key[0] ???
 
190
            delta = my_inv._make_delta(null_inv)
 
191
            serializer.require_flags(*self._format_flags)
 
192
            return serializer.delta_to_lines(
 
193
                revision.NULL_REVISION, self.key, delta)
 
194
        raise errors.UnavailableRepresentation(self.key, storage_kind,
 
195
            self.storage_kind)
 
196
 
 
197
 
161
198
class AbsentContentFactory(ContentFactory):
162
199
    """A placeholder content factory for unavailable texts.
163
200
 
175
212
        self.key = key
176
213
        self.parents = None
177
214
 
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
215
 
185
216
class AdapterFactory(ContentFactory):
186
217
    """A content factory to adapt between key prefix's."""
913
944
        raise NotImplementedError(self.annotate)
914
945
 
915
946
    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
 
        """
 
947
        """Check this object for integrity."""
926
948
        raise NotImplementedError(self.check)
927
949
 
928
950
    @staticmethod
929
951
    def check_not_reserved_id(version_id):
930
952
        revision.check_not_reserved_id(version_id)
931
953
 
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
954
    def _check_lines_not_unicode(self, lines):
940
955
        """Check that lines being added to a versioned file are not unicode."""
941
956
        for line in lines:
948
963
            if '\n' in line[:-1]:
949
964
                raise errors.BzrBadParameterContainsNewline("lines")
950
965
 
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
966
    def get_parent_map(self, keys):
966
967
        """Get a map of the parents of keys.
967
968
 
1162
1163
    def get_annotator(self):
1163
1164
        return annotate.Annotator(self)
1164
1165
 
1165
 
    def check(self, progress_bar=None, keys=None):
 
1166
    def check(self, progress_bar=None):
1166
1167
        """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
1168
        for prefix, vf in self._iter_all_components():
1171
1169
            vf.check()
1172
 
        if keys is not None:
1173
 
            return self.get_record_stream(keys, 'unordered', True)
1174
1170
 
1175
1171
    def get_parent_map(self, keys):
1176
1172
        """Get a map of the parents of keys.
1425
1421
    def __init__(self, plan, a_marker=TextMerge.A_MARKER,
1426
1422
                 b_marker=TextMerge.B_MARKER):
1427
1423
        TextMerge.__init__(self, a_marker, b_marker)
1428
 
        self.plan = list(plan)
 
1424
        self.plan = plan
1429
1425
 
1430
1426
    def _merge_struct(self):
1431
1427
        lines_a = []
1489
1485
        for struct in outstanding_struct():
1490
1486
            yield struct
1491
1487
 
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
1488
 
1547
1489
class WeaveMerge(PlanWeaveMerge):
1548
1490
    """Weave merge that takes a VersionedFile and two versions as its input."""
1649
1591
        self._kind_factory = {
1650
1592
            'fulltext': fulltext_network_to_record,
1651
1593
            'groupcompress-block': groupcompress.network_block_to_records,
 
1594
            'inventory-delta': inventory_delta_network_to_record,
1652
1595
            'knit-ft-gz': knit.knit_network_to_record,
1653
1596
            'knit-delta-gz': knit.knit_network_to_record,
1654
1597
            'knit-annotated-ft-gz': knit.knit_network_to_record,
1679
1622
    return [FulltextContentFactory(key, parents, None, fulltext)]
1680
1623
 
1681
1624
 
 
1625
def inventory_delta_network_to_record(kind, bytes, line_end):
 
1626
    """Convert a network inventory-delta record to record."""
 
1627
    meta_len, = struct.unpack('!L', bytes[line_end:line_end+4])
 
1628
    record_meta = bytes[line_end+4:line_end+4+meta_len]
 
1629
    key, parents = bencode.bdecode_as_tuple(record_meta)
 
1630
    if parents == 'nil':
 
1631
        parents = None
 
1632
    inventory_delta_bytes = bytes[line_end+4+meta_len:]
 
1633
    deserialiser = inventory_delta.InventoryDeltaSerializer()
 
1634
    parse_result = deserialiser.parse_text_bytes(inventory_delta_bytes)
 
1635
    basis_id, new_id, rich_root, tree_refs, delta = parse_result
 
1636
    return [InventoryDeltaContentFactory(
 
1637
        key, parents, None, delta, basis_id, (rich_root, tree_refs))]
 
1638
 
 
1639
 
1682
1640
def _length_prefix(bytes):
1683
1641
    return struct.pack('!L', len(bytes))
1684
1642
 
1694
1652
        _length_prefix(record_meta), record_meta, record_content)
1695
1653
 
1696
1654
 
 
1655
def record_to_inventory_delta_bytes(record):
 
1656
    record_content = record.get_bytes_as('inventory-delta-bytes')
 
1657
    if record.parents is None:
 
1658
        parents = 'nil'
 
1659
    else:
 
1660
        parents = record.parents
 
1661
    record_meta = bencode.bencode((record.key, parents))
 
1662
    return "inventory-delta\n%s%s%s" % (
 
1663
        _length_prefix(record_meta), record_meta, record_content)
 
1664
 
 
1665
 
1697
1666
def sort_groupcompress(parent_map):
1698
1667
    """Sort and group the keys in parent_map into groupcompress order.
1699
1668