~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/pack_repo.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-05-28 00:25:32 UTC
  • mfrom: (5264.1.2 command-help-bug-177500)
  • Revision ID: pqm@pqm.ubuntu.com-20100528002532-9bzj1fajyxckd1rg
(lifeless) Stop raising at runtime when a command has no help,
 instead have a test in the test suite that checks all known command objects.
 (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011 Canonical Ltd
 
1
# Copyright (C) 2007-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
30
30
    osutils,
31
31
    pack,
32
32
    transactions,
33
 
    tsort,
34
33
    ui,
 
34
    xml5,
 
35
    xml6,
 
36
    xml7,
35
37
    )
36
38
from bzrlib.index import (
37
39
    CombinedGraphIndex,
38
40
    GraphIndexPrefixAdapter,
39
41
    )
 
42
from bzrlib.knit import (
 
43
    KnitPlainFactory,
 
44
    KnitVersionedFiles,
 
45
    _KnitGraphIndex,
 
46
    _DirectPackAccess,
 
47
    )
 
48
from bzrlib import tsort
40
49
""")
41
50
from bzrlib import (
42
 
    btree_index,
 
51
    bzrdir,
43
52
    errors,
44
53
    lockable_files,
45
54
    lockdir,
 
55
    revision as _mod_revision,
46
56
    )
47
57
 
48
 
from bzrlib.decorators import (
49
 
    needs_read_lock,
50
 
    needs_write_lock,
51
 
    only_raises,
 
58
from bzrlib.decorators import needs_write_lock, only_raises
 
59
from bzrlib.btree_index import (
 
60
    BTreeGraphIndex,
 
61
    BTreeBuilder,
 
62
    )
 
63
from bzrlib.index import (
 
64
    GraphIndex,
 
65
    InMemoryGraphIndex,
52
66
    )
53
67
from bzrlib.lock import LogicalLockResult
 
68
from bzrlib.repofmt.knitrepo import KnitRepository
54
69
from bzrlib.repository import (
55
 
    _LazyListJoin,
56
 
    MetaDirRepository,
 
70
    CommitBuilder,
 
71
    MetaDirRepositoryFormat,
57
72
    RepositoryFormat,
58
73
    RepositoryWriteLockResult,
59
 
    )
60
 
from bzrlib.vf_repository import (
61
 
    MetaDirVersionedFileRepository,
62
 
    MetaDirVersionedFileRepositoryFormat,
63
 
    VersionedFileCommitBuilder,
64
 
    VersionedFileRootCommitBuilder,
 
74
    RootCommitBuilder,
 
75
    StreamSource,
65
76
    )
66
77
from bzrlib.trace import (
67
78
    mutter,
70
81
    )
71
82
 
72
83
 
73
 
class PackCommitBuilder(VersionedFileCommitBuilder):
74
 
    """Subclass of VersionedFileCommitBuilder to add texts with pack semantics.
 
84
class PackCommitBuilder(CommitBuilder):
 
85
    """A subclass of CommitBuilder to add texts with pack semantics.
75
86
 
76
87
    Specifically this uses one knit object rather than one knit object per
77
88
    added text, reducing memory and object pressure.
79
90
 
80
91
    def __init__(self, repository, parents, config, timestamp=None,
81
92
                 timezone=None, committer=None, revprops=None,
82
 
                 revision_id=None, lossy=False):
83
 
        VersionedFileCommitBuilder.__init__(self, repository, parents, config,
 
93
                 revision_id=None):
 
94
        CommitBuilder.__init__(self, repository, parents, config,
84
95
            timestamp=timestamp, timezone=timezone, committer=committer,
85
 
            revprops=revprops, revision_id=revision_id, lossy=lossy)
 
96
            revprops=revprops, revision_id=revision_id)
86
97
        self._file_graph = graph.Graph(
87
98
            repository._pack_collection.text_index.combined_index)
88
99
 
91
102
        return set([key[1] for key in self._file_graph.heads(keys)])
92
103
 
93
104
 
94
 
class PackRootCommitBuilder(VersionedFileRootCommitBuilder):
 
105
class PackRootCommitBuilder(RootCommitBuilder):
95
106
    """A subclass of RootCommitBuilder to add texts with pack semantics.
96
107
 
97
108
    Specifically this uses one knit object rather than one knit object per
100
111
 
101
112
    def __init__(self, repository, parents, config, timestamp=None,
102
113
                 timezone=None, committer=None, revprops=None,
103
 
                 revision_id=None, lossy=False):
104
 
        super(PackRootCommitBuilder, self).__init__(repository, parents,
105
 
            config, timestamp=timestamp, timezone=timezone,
106
 
            committer=committer, revprops=revprops, revision_id=revision_id,
107
 
            lossy=lossy)
 
114
                 revision_id=None):
 
115
        CommitBuilder.__init__(self, repository, parents, config,
 
116
            timestamp=timestamp, timezone=timezone, committer=committer,
 
117
            revprops=revprops, revision_id=revision_id)
108
118
        self._file_graph = graph.Graph(
109
119
            repository._pack_collection.text_index.combined_index)
110
120
 
221
231
        unlimited_cache = False
222
232
        if index_type == 'chk':
223
233
            unlimited_cache = True
224
 
        index = self.index_class(self.index_transport,
225
 
                    self.index_name(index_type, self.name),
226
 
                    self.index_sizes[self.index_offset(index_type)],
227
 
                    unlimited_cache=unlimited_cache)
228
 
        if index_type == 'chk':
229
 
            index._leaf_factory = btree_index._gcchk_factory
230
 
        setattr(self, index_type + '_index', index)
 
234
        setattr(self, index_type + '_index',
 
235
            self.index_class(self.index_transport,
 
236
                self.index_name(index_type, self.name),
 
237
                self.index_sizes[self.index_offset(index_type)],
 
238
                unlimited_cache=unlimited_cache))
231
239
 
232
240
 
233
241
class ExistingPack(Pack):
314
322
        for index_type in index_types:
315
323
            old_name = self.index_name(index_type, self.name)
316
324
            new_name = '../indices/' + old_name
317
 
            self.upload_transport.move(old_name, new_name)
 
325
            self.upload_transport.rename(old_name, new_name)
318
326
            self._replace_index_with_readonly(index_type)
319
327
        new_name = '../packs/' + self.file_name()
320
 
        self.upload_transport.move(self.file_name(), new_name)
 
328
        self.upload_transport.rename(self.file_name(), new_name)
321
329
        self._state = 'finished'
322
330
 
323
331
    def _get_external_refs(self, index):
503
511
        new_name = self.name + '.pack'
504
512
        if not suspend:
505
513
            new_name = '../packs/' + new_name
506
 
        self.upload_transport.move(self.random_name, new_name)
 
514
        self.upload_transport.rename(self.random_name, new_name)
507
515
        self._state = 'finished'
508
516
        if 'pack' in debug.debug_flags:
509
517
            # XXX: size might be interesting?
666
674
        # What text keys to copy. None for 'all texts'. This is set by
667
675
        # _copy_inventory_texts
668
676
        self._text_filter = None
 
677
        self._extra_init()
 
678
 
 
679
    def _extra_init(self):
 
680
        """A template hook to allow extending the constructor trivially."""
 
681
 
 
682
    def _pack_map_and_index_list(self, index_attribute):
 
683
        """Convert a list of packs to an index pack map and index list.
 
684
 
 
685
        :param index_attribute: The attribute that the desired index is found
 
686
            on.
 
687
        :return: A tuple (map, list) where map contains the dict from
 
688
            index:pack_tuple, and list contains the indices in the preferred
 
689
            access order.
 
690
        """
 
691
        indices = []
 
692
        pack_map = {}
 
693
        for pack_obj in self.packs:
 
694
            index = getattr(pack_obj, index_attribute)
 
695
            indices.append(index)
 
696
            pack_map[index] = pack_obj
 
697
        return pack_map, indices
 
698
 
 
699
    def _index_contents(self, indices, key_filter=None):
 
700
        """Get an iterable of the index contents from a pack_map.
 
701
 
 
702
        :param indices: The list of indices to query
 
703
        :param key_filter: An optional filter to limit the keys returned.
 
704
        """
 
705
        all_index = CombinedGraphIndex(indices)
 
706
        if key_filter is None:
 
707
            return all_index.iter_all_entries()
 
708
        else:
 
709
            return all_index.iter_entries(key_filter)
669
710
 
670
711
    def pack(self, pb=None):
671
712
        """Create a new pack by reading data from other packs.
682
723
        :return: A Pack object, or None if nothing was copied.
683
724
        """
684
725
        # open a pack - using the same name as the last temporary file
685
 
        # - which has already been flushed, so it's safe.
 
726
        # - which has already been flushed, so its safe.
686
727
        # XXX: - duplicate code warning with start_write_group; fix before
687
728
        #      considering 'done'.
688
729
        if self._pack_collection._new_pack is not None:
720
761
        new_pack.signature_index.set_optimize(combine_backing_indices=False)
721
762
        return new_pack
722
763
 
 
764
    def _update_pack_order(self, entries, index_to_pack_map):
 
765
        """Determine how we want our packs to be ordered.
 
766
 
 
767
        This changes the sort order of the self.packs list so that packs unused
 
768
        by 'entries' will be at the end of the list, so that future requests
 
769
        can avoid probing them.  Used packs will be at the front of the
 
770
        self.packs list, in the order of their first use in 'entries'.
 
771
 
 
772
        :param entries: A list of (index, ...) tuples
 
773
        :param index_to_pack_map: A mapping from index objects to pack objects.
 
774
        """
 
775
        packs = []
 
776
        seen_indexes = set()
 
777
        for entry in entries:
 
778
            index = entry[0]
 
779
            if index not in seen_indexes:
 
780
                packs.append(index_to_pack_map[index])
 
781
                seen_indexes.add(index)
 
782
        if len(packs) == len(self.packs):
 
783
            if 'pack' in debug.debug_flags:
 
784
                mutter('Not changing pack list, all packs used.')
 
785
            return
 
786
        seen_packs = set(packs)
 
787
        for pack in self.packs:
 
788
            if pack not in seen_packs:
 
789
                packs.append(pack)
 
790
                seen_packs.add(pack)
 
791
        if 'pack' in debug.debug_flags:
 
792
            old_names = [p.access_tuple()[1] for p in self.packs]
 
793
            new_names = [p.access_tuple()[1] for p in packs]
 
794
            mutter('Reordering packs\nfrom: %s\n  to: %s',
 
795
                   old_names, new_names)
 
796
        self.packs = packs
 
797
 
723
798
    def _copy_revision_texts(self):
724
799
        """Copy revision data to the new pack."""
725
 
        raise NotImplementedError(self._copy_revision_texts)
 
800
        # select revisions
 
801
        if self.revision_ids:
 
802
            revision_keys = [(revision_id,) for revision_id in self.revision_ids]
 
803
        else:
 
804
            revision_keys = None
 
805
        # select revision keys
 
806
        revision_index_map, revision_indices = self._pack_map_and_index_list(
 
807
            'revision_index')
 
808
        revision_nodes = self._index_contents(revision_indices, revision_keys)
 
809
        revision_nodes = list(revision_nodes)
 
810
        self._update_pack_order(revision_nodes, revision_index_map)
 
811
        # copy revision keys and adjust values
 
812
        self.pb.update("Copying revision texts", 1)
 
813
        total_items, readv_group_iter = self._revision_node_readv(revision_nodes)
 
814
        list(self._copy_nodes_graph(revision_index_map, self.new_pack._writer,
 
815
            self.new_pack.revision_index, readv_group_iter, total_items))
 
816
        if 'pack' in debug.debug_flags:
 
817
            mutter('%s: create_pack: revisions copied: %s%s %d items t+%6.3fs',
 
818
                time.ctime(), self._pack_collection._upload_transport.base,
 
819
                self.new_pack.random_name,
 
820
                self.new_pack.revision_index.key_count(),
 
821
                time.time() - self.new_pack.start_time)
 
822
        self._revision_keys = revision_keys
726
823
 
727
824
    def _copy_inventory_texts(self):
728
825
        """Copy the inventory texts to the new pack.
731
828
 
732
829
        Sets self._text_filter appropriately.
733
830
        """
734
 
        raise NotImplementedError(self._copy_inventory_texts)
 
831
        # select inventory keys
 
832
        inv_keys = self._revision_keys # currently the same keyspace, and note that
 
833
        # querying for keys here could introduce a bug where an inventory item
 
834
        # is missed, so do not change it to query separately without cross
 
835
        # checking like the text key check below.
 
836
        inventory_index_map, inventory_indices = self._pack_map_and_index_list(
 
837
            'inventory_index')
 
838
        inv_nodes = self._index_contents(inventory_indices, inv_keys)
 
839
        # copy inventory keys and adjust values
 
840
        # XXX: Should be a helper function to allow different inv representation
 
841
        # at this point.
 
842
        self.pb.update("Copying inventory texts", 2)
 
843
        total_items, readv_group_iter = self._least_readv_node_readv(inv_nodes)
 
844
        # Only grab the output lines if we will be processing them
 
845
        output_lines = bool(self.revision_ids)
 
846
        inv_lines = self._copy_nodes_graph(inventory_index_map,
 
847
            self.new_pack._writer, self.new_pack.inventory_index,
 
848
            readv_group_iter, total_items, output_lines=output_lines)
 
849
        if self.revision_ids:
 
850
            self._process_inventory_lines(inv_lines)
 
851
        else:
 
852
            # eat the iterator to cause it to execute.
 
853
            list(inv_lines)
 
854
            self._text_filter = None
 
855
        if 'pack' in debug.debug_flags:
 
856
            mutter('%s: create_pack: inventories copied: %s%s %d items t+%6.3fs',
 
857
                time.ctime(), self._pack_collection._upload_transport.base,
 
858
                self.new_pack.random_name,
 
859
                self.new_pack.inventory_index.key_count(),
 
860
                time.time() - self.new_pack.start_time)
735
861
 
736
862
    def _copy_text_texts(self):
737
 
        raise NotImplementedError(self._copy_text_texts)
 
863
        # select text keys
 
864
        text_index_map, text_nodes = self._get_text_nodes()
 
865
        if self._text_filter is not None:
 
866
            # We could return the keys copied as part of the return value from
 
867
            # _copy_nodes_graph but this doesn't work all that well with the
 
868
            # need to get line output too, so we check separately, and as we're
 
869
            # going to buffer everything anyway, we check beforehand, which
 
870
            # saves reading knit data over the wire when we know there are
 
871
            # mising records.
 
872
            text_nodes = set(text_nodes)
 
873
            present_text_keys = set(_node[1] for _node in text_nodes)
 
874
            missing_text_keys = set(self._text_filter) - present_text_keys
 
875
            if missing_text_keys:
 
876
                # TODO: raise a specific error that can handle many missing
 
877
                # keys.
 
878
                mutter("missing keys during fetch: %r", missing_text_keys)
 
879
                a_missing_key = missing_text_keys.pop()
 
880
                raise errors.RevisionNotPresent(a_missing_key[1],
 
881
                    a_missing_key[0])
 
882
        # copy text keys and adjust values
 
883
        self.pb.update("Copying content texts", 3)
 
884
        total_items, readv_group_iter = self._least_readv_node_readv(text_nodes)
 
885
        list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
 
886
            self.new_pack.text_index, readv_group_iter, total_items))
 
887
        self._log_copied_texts()
738
888
 
739
889
    def _create_pack_from_packs(self):
740
 
        raise NotImplementedError(self._create_pack_from_packs)
 
890
        self.pb.update("Opening pack", 0, 5)
 
891
        self.new_pack = self.open_pack()
 
892
        new_pack = self.new_pack
 
893
        # buffer data - we won't be reading-back during the pack creation and
 
894
        # this makes a significant difference on sftp pushes.
 
895
        new_pack.set_write_cache_size(1024*1024)
 
896
        if 'pack' in debug.debug_flags:
 
897
            plain_pack_list = ['%s%s' % (a_pack.pack_transport.base, a_pack.name)
 
898
                for a_pack in self.packs]
 
899
            if self.revision_ids is not None:
 
900
                rev_count = len(self.revision_ids)
 
901
            else:
 
902
                rev_count = 'all'
 
903
            mutter('%s: create_pack: creating pack from source packs: '
 
904
                '%s%s %s revisions wanted %s t=0',
 
905
                time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
 
906
                plain_pack_list, rev_count)
 
907
        self._copy_revision_texts()
 
908
        self._copy_inventory_texts()
 
909
        self._copy_text_texts()
 
910
        # select signature keys
 
911
        signature_filter = self._revision_keys # same keyspace
 
912
        signature_index_map, signature_indices = self._pack_map_and_index_list(
 
913
            'signature_index')
 
914
        signature_nodes = self._index_contents(signature_indices,
 
915
            signature_filter)
 
916
        # copy signature keys and adjust values
 
917
        self.pb.update("Copying signature texts", 4)
 
918
        self._copy_nodes(signature_nodes, signature_index_map, new_pack._writer,
 
919
            new_pack.signature_index)
 
920
        if 'pack' in debug.debug_flags:
 
921
            mutter('%s: create_pack: revision signatures copied: %s%s %d items t+%6.3fs',
 
922
                time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
 
923
                new_pack.signature_index.key_count(),
 
924
                time.time() - new_pack.start_time)
 
925
        # copy chk contents
 
926
        # NB XXX: how to check CHK references are present? perhaps by yielding
 
927
        # the items? How should that interact with stacked repos?
 
928
        if new_pack.chk_index is not None:
 
929
            self._copy_chks()
 
930
            if 'pack' in debug.debug_flags:
 
931
                mutter('%s: create_pack: chk content copied: %s%s %d items t+%6.3fs',
 
932
                    time.ctime(), self._pack_collection._upload_transport.base,
 
933
                    new_pack.random_name,
 
934
                    new_pack.chk_index.key_count(),
 
935
                    time.time() - new_pack.start_time)
 
936
        new_pack._check_references()
 
937
        if not self._use_pack(new_pack):
 
938
            new_pack.abort()
 
939
            return None
 
940
        self.pb.update("Finishing pack", 5)
 
941
        new_pack.finish()
 
942
        self._pack_collection.allocate(new_pack)
 
943
        return new_pack
 
944
 
 
945
    def _copy_chks(self, refs=None):
 
946
        # XXX: Todo, recursive follow-pointers facility when fetching some
 
947
        # revisions only.
 
948
        chk_index_map, chk_indices = self._pack_map_and_index_list(
 
949
            'chk_index')
 
950
        chk_nodes = self._index_contents(chk_indices, refs)
 
951
        new_refs = set()
 
952
        # TODO: This isn't strictly tasteful as we are accessing some private
 
953
        #       variables (_serializer). Perhaps a better way would be to have
 
954
        #       Repository._deserialise_chk_node()
 
955
        search_key_func = chk_map.search_key_registry.get(
 
956
            self._pack_collection.repo._serializer.search_key_name)
 
957
        def accumlate_refs(lines):
 
958
            # XXX: move to a generic location
 
959
            # Yay mismatch:
 
960
            bytes = ''.join(lines)
 
961
            node = chk_map._deserialise(bytes, ("unknown",), search_key_func)
 
962
            new_refs.update(node.refs())
 
963
        self._copy_nodes(chk_nodes, chk_index_map, self.new_pack._writer,
 
964
            self.new_pack.chk_index, output_lines=accumlate_refs)
 
965
        return new_refs
 
966
 
 
967
    def _copy_nodes(self, nodes, index_map, writer, write_index,
 
968
        output_lines=None):
 
969
        """Copy knit nodes between packs with no graph references.
 
970
 
 
971
        :param output_lines: Output full texts of copied items.
 
972
        """
 
973
        pb = ui.ui_factory.nested_progress_bar()
 
974
        try:
 
975
            return self._do_copy_nodes(nodes, index_map, writer,
 
976
                write_index, pb, output_lines=output_lines)
 
977
        finally:
 
978
            pb.finished()
 
979
 
 
980
    def _do_copy_nodes(self, nodes, index_map, writer, write_index, pb,
 
981
        output_lines=None):
 
982
        # for record verification
 
983
        knit = KnitVersionedFiles(None, None)
 
984
        # plan a readv on each source pack:
 
985
        # group by pack
 
986
        nodes = sorted(nodes)
 
987
        # how to map this into knit.py - or knit.py into this?
 
988
        # we don't want the typical knit logic, we want grouping by pack
 
989
        # at this point - perhaps a helper library for the following code
 
990
        # duplication points?
 
991
        request_groups = {}
 
992
        for index, key, value in nodes:
 
993
            if index not in request_groups:
 
994
                request_groups[index] = []
 
995
            request_groups[index].append((key, value))
 
996
        record_index = 0
 
997
        pb.update("Copied record", record_index, len(nodes))
 
998
        for index, items in request_groups.iteritems():
 
999
            pack_readv_requests = []
 
1000
            for key, value in items:
 
1001
                # ---- KnitGraphIndex.get_position
 
1002
                bits = value[1:].split(' ')
 
1003
                offset, length = int(bits[0]), int(bits[1])
 
1004
                pack_readv_requests.append((offset, length, (key, value[0])))
 
1005
            # linear scan up the pack
 
1006
            pack_readv_requests.sort()
 
1007
            # copy the data
 
1008
            pack_obj = index_map[index]
 
1009
            transport, path = pack_obj.access_tuple()
 
1010
            try:
 
1011
                reader = pack.make_readv_reader(transport, path,
 
1012
                    [offset[0:2] for offset in pack_readv_requests])
 
1013
            except errors.NoSuchFile:
 
1014
                if self._reload_func is not None:
 
1015
                    self._reload_func()
 
1016
                raise
 
1017
            for (names, read_func), (_1, _2, (key, eol_flag)) in \
 
1018
                izip(reader.iter_records(), pack_readv_requests):
 
1019
                raw_data = read_func(None)
 
1020
                # check the header only
 
1021
                if output_lines is not None:
 
1022
                    output_lines(knit._parse_record(key[-1], raw_data)[0])
 
1023
                else:
 
1024
                    df, _ = knit._parse_record_header(key, raw_data)
 
1025
                    df.close()
 
1026
                pos, size = writer.add_bytes_record(raw_data, names)
 
1027
                write_index.add_node(key, eol_flag + "%d %d" % (pos, size))
 
1028
                pb.update("Copied record", record_index)
 
1029
                record_index += 1
 
1030
 
 
1031
    def _copy_nodes_graph(self, index_map, writer, write_index,
 
1032
        readv_group_iter, total_items, output_lines=False):
 
1033
        """Copy knit nodes between packs.
 
1034
 
 
1035
        :param output_lines: Return lines present in the copied data as
 
1036
            an iterator of line,version_id.
 
1037
        """
 
1038
        pb = ui.ui_factory.nested_progress_bar()
 
1039
        try:
 
1040
            for result in self._do_copy_nodes_graph(index_map, writer,
 
1041
                write_index, output_lines, pb, readv_group_iter, total_items):
 
1042
                yield result
 
1043
        except Exception:
 
1044
            # Python 2.4 does not permit try:finally: in a generator.
 
1045
            pb.finished()
 
1046
            raise
 
1047
        else:
 
1048
            pb.finished()
 
1049
 
 
1050
    def _do_copy_nodes_graph(self, index_map, writer, write_index,
 
1051
        output_lines, pb, readv_group_iter, total_items):
 
1052
        # for record verification
 
1053
        knit = KnitVersionedFiles(None, None)
 
1054
        # for line extraction when requested (inventories only)
 
1055
        if output_lines:
 
1056
            factory = KnitPlainFactory()
 
1057
        record_index = 0
 
1058
        pb.update("Copied record", record_index, total_items)
 
1059
        for index, readv_vector, node_vector in readv_group_iter:
 
1060
            # copy the data
 
1061
            pack_obj = index_map[index]
 
1062
            transport, path = pack_obj.access_tuple()
 
1063
            try:
 
1064
                reader = pack.make_readv_reader(transport, path, readv_vector)
 
1065
            except errors.NoSuchFile:
 
1066
                if self._reload_func is not None:
 
1067
                    self._reload_func()
 
1068
                raise
 
1069
            for (names, read_func), (key, eol_flag, references) in \
 
1070
                izip(reader.iter_records(), node_vector):
 
1071
                raw_data = read_func(None)
 
1072
                if output_lines:
 
1073
                    # read the entire thing
 
1074
                    content, _ = knit._parse_record(key[-1], raw_data)
 
1075
                    if len(references[-1]) == 0:
 
1076
                        line_iterator = factory.get_fulltext_content(content)
 
1077
                    else:
 
1078
                        line_iterator = factory.get_linedelta_content(content)
 
1079
                    for line in line_iterator:
 
1080
                        yield line, key
 
1081
                else:
 
1082
                    # check the header only
 
1083
                    df, _ = knit._parse_record_header(key, raw_data)
 
1084
                    df.close()
 
1085
                pos, size = writer.add_bytes_record(raw_data, names)
 
1086
                write_index.add_node(key, eol_flag + "%d %d" % (pos, size), references)
 
1087
                pb.update("Copied record", record_index)
 
1088
                record_index += 1
 
1089
 
 
1090
    def _get_text_nodes(self):
 
1091
        text_index_map, text_indices = self._pack_map_and_index_list(
 
1092
            'text_index')
 
1093
        return text_index_map, self._index_contents(text_indices,
 
1094
            self._text_filter)
 
1095
 
 
1096
    def _least_readv_node_readv(self, nodes):
 
1097
        """Generate request groups for nodes using the least readv's.
 
1098
 
 
1099
        :param nodes: An iterable of graph index nodes.
 
1100
        :return: Total node count and an iterator of the data needed to perform
 
1101
            readvs to obtain the data for nodes. Each item yielded by the
 
1102
            iterator is a tuple with:
 
1103
            index, readv_vector, node_vector. readv_vector is a list ready to
 
1104
            hand to the transport readv method, and node_vector is a list of
 
1105
            (key, eol_flag, references) for the node retrieved by the
 
1106
            matching readv_vector.
 
1107
        """
 
1108
        # group by pack so we do one readv per pack
 
1109
        nodes = sorted(nodes)
 
1110
        total = len(nodes)
 
1111
        request_groups = {}
 
1112
        for index, key, value, references in nodes:
 
1113
            if index not in request_groups:
 
1114
                request_groups[index] = []
 
1115
            request_groups[index].append((key, value, references))
 
1116
        result = []
 
1117
        for index, items in request_groups.iteritems():
 
1118
            pack_readv_requests = []
 
1119
            for key, value, references in items:
 
1120
                # ---- KnitGraphIndex.get_position
 
1121
                bits = value[1:].split(' ')
 
1122
                offset, length = int(bits[0]), int(bits[1])
 
1123
                pack_readv_requests.append(
 
1124
                    ((offset, length), (key, value[0], references)))
 
1125
            # linear scan up the pack to maximum range combining.
 
1126
            pack_readv_requests.sort()
 
1127
            # split out the readv and the node data.
 
1128
            pack_readv = [readv for readv, node in pack_readv_requests]
 
1129
            node_vector = [node for readv, node in pack_readv_requests]
 
1130
            result.append((index, pack_readv, node_vector))
 
1131
        return total, result
741
1132
 
742
1133
    def _log_copied_texts(self):
743
1134
        if 'pack' in debug.debug_flags:
747
1138
                self.new_pack.text_index.key_count(),
748
1139
                time.time() - self.new_pack.start_time)
749
1140
 
 
1141
    def _process_inventory_lines(self, inv_lines):
 
1142
        """Use up the inv_lines generator and setup a text key filter."""
 
1143
        repo = self._pack_collection.repo
 
1144
        fileid_revisions = repo._find_file_ids_from_xml_inventory_lines(
 
1145
            inv_lines, self.revision_keys)
 
1146
        text_filter = []
 
1147
        for fileid, file_revids in fileid_revisions.iteritems():
 
1148
            text_filter.extend([(fileid, file_revid) for file_revid in file_revids])
 
1149
        self._text_filter = text_filter
 
1150
 
 
1151
    def _revision_node_readv(self, revision_nodes):
 
1152
        """Return the total revisions and the readv's to issue.
 
1153
 
 
1154
        :param revision_nodes: The revision index contents for the packs being
 
1155
            incorporated into the new pack.
 
1156
        :return: As per _least_readv_node_readv.
 
1157
        """
 
1158
        return self._least_readv_node_readv(revision_nodes)
 
1159
 
750
1160
    def _use_pack(self, new_pack):
751
1161
        """Return True if new_pack should be used.
752
1162
 
756
1166
        return new_pack.data_inserted()
757
1167
 
758
1168
 
 
1169
class OptimisingPacker(Packer):
 
1170
    """A packer which spends more time to create better disk layouts."""
 
1171
 
 
1172
    def _revision_node_readv(self, revision_nodes):
 
1173
        """Return the total revisions and the readv's to issue.
 
1174
 
 
1175
        This sort places revisions in topological order with the ancestors
 
1176
        after the children.
 
1177
 
 
1178
        :param revision_nodes: The revision index contents for the packs being
 
1179
            incorporated into the new pack.
 
1180
        :return: As per _least_readv_node_readv.
 
1181
        """
 
1182
        # build an ancestors dict
 
1183
        ancestors = {}
 
1184
        by_key = {}
 
1185
        for index, key, value, references in revision_nodes:
 
1186
            ancestors[key] = references[0]
 
1187
            by_key[key] = (index, value, references)
 
1188
        order = tsort.topo_sort(ancestors)
 
1189
        total = len(order)
 
1190
        # Single IO is pathological, but it will work as a starting point.
 
1191
        requests = []
 
1192
        for key in reversed(order):
 
1193
            index, value, references = by_key[key]
 
1194
            # ---- KnitGraphIndex.get_position
 
1195
            bits = value[1:].split(' ')
 
1196
            offset, length = int(bits[0]), int(bits[1])
 
1197
            requests.append(
 
1198
                (index, [(offset, length)], [(key, value[0], references)]))
 
1199
        # TODO: combine requests in the same index that are in ascending order.
 
1200
        return total, requests
 
1201
 
 
1202
    def open_pack(self):
 
1203
        """Open a pack for the pack we are creating."""
 
1204
        new_pack = super(OptimisingPacker, self).open_pack()
 
1205
        # Turn on the optimization flags for all the index builders.
 
1206
        new_pack.revision_index.set_optimize(for_size=True)
 
1207
        new_pack.inventory_index.set_optimize(for_size=True)
 
1208
        new_pack.text_index.set_optimize(for_size=True)
 
1209
        new_pack.signature_index.set_optimize(for_size=True)
 
1210
        return new_pack
 
1211
 
 
1212
 
 
1213
class ReconcilePacker(Packer):
 
1214
    """A packer which regenerates indices etc as it copies.
 
1215
 
 
1216
    This is used by ``bzr reconcile`` to cause parent text pointers to be
 
1217
    regenerated.
 
1218
    """
 
1219
 
 
1220
    def _extra_init(self):
 
1221
        self._data_changed = False
 
1222
 
 
1223
    def _process_inventory_lines(self, inv_lines):
 
1224
        """Generate a text key reference map rather for reconciling with."""
 
1225
        repo = self._pack_collection.repo
 
1226
        refs = repo._find_text_key_references_from_xml_inventory_lines(
 
1227
            inv_lines)
 
1228
        self._text_refs = refs
 
1229
        # during reconcile we:
 
1230
        #  - convert unreferenced texts to full texts
 
1231
        #  - correct texts which reference a text not copied to be full texts
 
1232
        #  - copy all others as-is but with corrected parents.
 
1233
        #  - so at this point we don't know enough to decide what becomes a full
 
1234
        #    text.
 
1235
        self._text_filter = None
 
1236
 
 
1237
    def _copy_text_texts(self):
 
1238
        """generate what texts we should have and then copy."""
 
1239
        self.pb.update("Copying content texts", 3)
 
1240
        # we have three major tasks here:
 
1241
        # 1) generate the ideal index
 
1242
        repo = self._pack_collection.repo
 
1243
        ancestors = dict([(key[0], tuple(ref[0] for ref in refs[0])) for
 
1244
            _1, key, _2, refs in
 
1245
            self.new_pack.revision_index.iter_all_entries()])
 
1246
        ideal_index = repo._generate_text_key_index(self._text_refs, ancestors)
 
1247
        # 2) generate a text_nodes list that contains all the deltas that can
 
1248
        #    be used as-is, with corrected parents.
 
1249
        ok_nodes = []
 
1250
        bad_texts = []
 
1251
        discarded_nodes = []
 
1252
        NULL_REVISION = _mod_revision.NULL_REVISION
 
1253
        text_index_map, text_nodes = self._get_text_nodes()
 
1254
        for node in text_nodes:
 
1255
            # 0 - index
 
1256
            # 1 - key
 
1257
            # 2 - value
 
1258
            # 3 - refs
 
1259
            try:
 
1260
                ideal_parents = tuple(ideal_index[node[1]])
 
1261
            except KeyError:
 
1262
                discarded_nodes.append(node)
 
1263
                self._data_changed = True
 
1264
            else:
 
1265
                if ideal_parents == (NULL_REVISION,):
 
1266
                    ideal_parents = ()
 
1267
                if ideal_parents == node[3][0]:
 
1268
                    # no change needed.
 
1269
                    ok_nodes.append(node)
 
1270
                elif ideal_parents[0:1] == node[3][0][0:1]:
 
1271
                    # the left most parent is the same, or there are no parents
 
1272
                    # today. Either way, we can preserve the representation as
 
1273
                    # long as we change the refs to be inserted.
 
1274
                    self._data_changed = True
 
1275
                    ok_nodes.append((node[0], node[1], node[2],
 
1276
                        (ideal_parents, node[3][1])))
 
1277
                    self._data_changed = True
 
1278
                else:
 
1279
                    # Reinsert this text completely
 
1280
                    bad_texts.append((node[1], ideal_parents))
 
1281
                    self._data_changed = True
 
1282
        # we're finished with some data.
 
1283
        del ideal_index
 
1284
        del text_nodes
 
1285
        # 3) bulk copy the ok data
 
1286
        total_items, readv_group_iter = self._least_readv_node_readv(ok_nodes)
 
1287
        list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
 
1288
            self.new_pack.text_index, readv_group_iter, total_items))
 
1289
        # 4) adhoc copy all the other texts.
 
1290
        # We have to topologically insert all texts otherwise we can fail to
 
1291
        # reconcile when parts of a single delta chain are preserved intact,
 
1292
        # and other parts are not. E.g. Discarded->d1->d2->d3. d1 will be
 
1293
        # reinserted, and if d3 has incorrect parents it will also be
 
1294
        # reinserted. If we insert d3 first, d2 is present (as it was bulk
 
1295
        # copied), so we will try to delta, but d2 is not currently able to be
 
1296
        # extracted because it's basis d1 is not present. Topologically sorting
 
1297
        # addresses this. The following generates a sort for all the texts that
 
1298
        # are being inserted without having to reference the entire text key
 
1299
        # space (we only topo sort the revisions, which is smaller).
 
1300
        topo_order = tsort.topo_sort(ancestors)
 
1301
        rev_order = dict(zip(topo_order, range(len(topo_order))))
 
1302
        bad_texts.sort(key=lambda key:rev_order.get(key[0][1], 0))
 
1303
        transaction = repo.get_transaction()
 
1304
        file_id_index = GraphIndexPrefixAdapter(
 
1305
            self.new_pack.text_index,
 
1306
            ('blank', ), 1,
 
1307
            add_nodes_callback=self.new_pack.text_index.add_nodes)
 
1308
        data_access = _DirectPackAccess(
 
1309
                {self.new_pack.text_index:self.new_pack.access_tuple()})
 
1310
        data_access.set_writer(self.new_pack._writer, self.new_pack.text_index,
 
1311
            self.new_pack.access_tuple())
 
1312
        output_texts = KnitVersionedFiles(
 
1313
            _KnitGraphIndex(self.new_pack.text_index,
 
1314
                add_callback=self.new_pack.text_index.add_nodes,
 
1315
                deltas=True, parents=True, is_locked=repo.is_locked),
 
1316
            data_access=data_access, max_delta_chain=200)
 
1317
        for key, parent_keys in bad_texts:
 
1318
            # We refer to the new pack to delta data being output.
 
1319
            # A possible improvement would be to catch errors on short reads
 
1320
            # and only flush then.
 
1321
            self.new_pack.flush()
 
1322
            parents = []
 
1323
            for parent_key in parent_keys:
 
1324
                if parent_key[0] != key[0]:
 
1325
                    # Graph parents must match the fileid
 
1326
                    raise errors.BzrError('Mismatched key parent %r:%r' %
 
1327
                        (key, parent_keys))
 
1328
                parents.append(parent_key[1])
 
1329
            text_lines = osutils.split_lines(repo.texts.get_record_stream(
 
1330
                [key], 'unordered', True).next().get_bytes_as('fulltext'))
 
1331
            output_texts.add_lines(key, parent_keys, text_lines,
 
1332
                random_id=True, check_content=False)
 
1333
        # 5) check that nothing inserted has a reference outside the keyspace.
 
1334
        missing_text_keys = self.new_pack.text_index._external_references()
 
1335
        if missing_text_keys:
 
1336
            raise errors.BzrCheckError('Reference to missing compression parents %r'
 
1337
                % (missing_text_keys,))
 
1338
        self._log_copied_texts()
 
1339
 
 
1340
    def _use_pack(self, new_pack):
 
1341
        """Override _use_pack to check for reconcile having changed content."""
 
1342
        # XXX: we might be better checking this at the copy time.
 
1343
        original_inventory_keys = set()
 
1344
        inv_index = self._pack_collection.inventory_index.combined_index
 
1345
        for entry in inv_index.iter_all_entries():
 
1346
            original_inventory_keys.add(entry[1])
 
1347
        new_inventory_keys = set()
 
1348
        for entry in new_pack.inventory_index.iter_all_entries():
 
1349
            new_inventory_keys.add(entry[1])
 
1350
        if new_inventory_keys != original_inventory_keys:
 
1351
            self._data_changed = True
 
1352
        return new_pack.data_inserted() and self._data_changed
 
1353
 
 
1354
 
759
1355
class RepositoryPackCollection(object):
760
1356
    """Management of packs within a repository.
761
1357
 
762
1358
    :ivar _names: map of {pack_name: (index_size,)}
763
1359
    """
764
1360
 
765
 
    pack_factory = None
766
 
    resumed_pack_factory = None
767
 
    normal_packer_class = None
768
 
    optimising_packer_class = None
 
1361
    pack_factory = NewPack
 
1362
    resumed_pack_factory = ResumedPack
769
1363
 
770
1364
    def __init__(self, repo, transport, index_transport, upload_transport,
771
1365
                 pack_transport, index_builder_class, index_class,
912
1506
            'containing %d revisions. Packing %d files into %d affecting %d'
913
1507
            ' revisions', self, total_packs, total_revisions, num_old_packs,
914
1508
            num_new_packs, num_revs_affected)
915
 
        result = self._execute_pack_operations(pack_operations, packer_class=self.normal_packer_class,
 
1509
        result = self._execute_pack_operations(pack_operations,
916
1510
                                      reload_func=self._restart_autopack)
917
1511
        mutter('Auto-packing repository %s completed', self)
918
1512
        return result
919
1513
 
920
 
    def _execute_pack_operations(self, pack_operations, packer_class,
921
 
            reload_func=None):
 
1514
    def _execute_pack_operations(self, pack_operations, _packer_class=Packer,
 
1515
                                 reload_func=None):
922
1516
        """Execute a series of pack operations.
923
1517
 
924
1518
        :param pack_operations: A list of [revision_count, packs_to_combine].
925
 
        :param packer_class: The class of packer to use
 
1519
        :param _packer_class: The class of packer to use (default: Packer).
926
1520
        :return: The new pack names.
927
1521
        """
928
1522
        for revision_count, packs in pack_operations:
929
1523
            # we may have no-ops from the setup logic
930
1524
            if len(packs) == 0:
931
1525
                continue
932
 
            packer = packer_class(self, packs, '.autopack',
 
1526
            packer = _packer_class(self, packs, '.autopack',
933
1527
                                   reload_func=reload_func)
934
1528
            try:
935
 
                result = packer.pack()
 
1529
                packer.pack()
936
1530
            except errors.RetryWithNewPacks:
937
1531
                # An exception is propagating out of this context, make sure
938
1532
                # this packer has cleaned up. Packer() doesn't set its new_pack
941
1535
                if packer.new_pack is not None:
942
1536
                    packer.new_pack.abort()
943
1537
                raise
944
 
            if result is None:
945
 
                return
946
1538
            for pack in packs:
947
1539
                self._remove_pack_from_memory(pack)
948
1540
        # record the newly available packs and stop advertising the old
982
1574
        mutter('Packing repository %s, which has %d pack files, '
983
1575
            'containing %d revisions with hint %r.', self, total_packs,
984
1576
            total_revisions, hint)
985
 
        while True:
986
 
            try:
987
 
                self._try_pack_operations(hint)
988
 
            except RetryPackOperations:
989
 
                continue
990
 
            break
991
 
 
992
 
        if clean_obsolete_packs:
993
 
            self._clear_obsolete_packs()
994
 
 
995
 
    def _try_pack_operations(self, hint):
996
 
        """Calculate the pack operations based on the hint (if any), and
997
 
        execute them.
998
 
        """
999
1577
        # determine which packs need changing
1000
1578
        pack_operations = [[0, []]]
1001
1579
        for pack in self.all_packs():
1004
1582
                # or this pack was included in the hint.
1005
1583
                pack_operations[-1][0] += pack.get_revision_count()
1006
1584
                pack_operations[-1][1].append(pack)
1007
 
        self._execute_pack_operations(pack_operations,
1008
 
            packer_class=self.optimising_packer_class,
1009
 
            reload_func=self._restart_pack_operations)
 
1585
        self._execute_pack_operations(pack_operations, OptimisingPacker)
 
1586
 
 
1587
        if clean_obsolete_packs:
 
1588
            self._clear_obsolete_packs()
1010
1589
 
1011
1590
    def plan_autopack_combinations(self, existing_packs, pack_distribution):
1012
1591
        """Plan a pack operation.
1022
1601
        pack_operations = [[0, []]]
1023
1602
        # plan out what packs to keep, and what to reorganise
1024
1603
        while len(existing_packs):
1025
 
            # take the largest pack, and if it's less than the head of the
 
1604
            # take the largest pack, and if its less than the head of the
1026
1605
            # distribution chart we will include its contents in the new pack
1027
 
            # for that position. If it's larger, we remove its size from the
 
1606
            # for that position. If its larger, we remove its size from the
1028
1607
            # distribution chart
1029
1608
            next_pack_rev_count, next_pack = existing_packs.pop(0)
1030
1609
            if next_pack_rev_count >= pack_distribution[0]:
1065
1644
 
1066
1645
        :return: True if the disk names had not been previously read.
1067
1646
        """
1068
 
        # NB: if you see an assertion error here, it's probably access against
 
1647
        # NB: if you see an assertion error here, its probably access against
1069
1648
        # an unlocked repo. Naughty.
1070
1649
        if not self.repo.is_locked():
1071
1650
            raise errors.ObjectNotLocked(self.repo)
1101
1680
            txt_index = self._make_index(name, '.tix')
1102
1681
            sig_index = self._make_index(name, '.six')
1103
1682
            if self.chk_index is not None:
1104
 
                chk_index = self._make_index(name, '.cix', is_chk=True)
 
1683
                chk_index = self._make_index(name, '.cix', unlimited_cache=True)
1105
1684
            else:
1106
1685
                chk_index = None
1107
1686
            result = ExistingPack(self._pack_transport, name, rev_index,
1127
1706
            sig_index = self._make_index(name, '.six', resume=True)
1128
1707
            if self.chk_index is not None:
1129
1708
                chk_index = self._make_index(name, '.cix', resume=True,
1130
 
                                             is_chk=True)
 
1709
                                             unlimited_cache=True)
1131
1710
            else:
1132
1711
                chk_index = None
1133
1712
            result = self.resumed_pack_factory(name, rev_index, inv_index,
1163
1742
        return self._index_class(self.transport, 'pack-names', None
1164
1743
                ).iter_all_entries()
1165
1744
 
1166
 
    def _make_index(self, name, suffix, resume=False, is_chk=False):
 
1745
    def _make_index(self, name, suffix, resume=False, unlimited_cache=False):
1167
1746
        size_offset = self._suffix_offsets[suffix]
1168
1747
        index_name = name + suffix
1169
1748
        if resume:
1172
1751
        else:
1173
1752
            transport = self._index_transport
1174
1753
            index_size = self._names[name][size_offset]
1175
 
        index = self._index_class(transport, index_name, index_size,
1176
 
                                  unlimited_cache=is_chk)
1177
 
        if is_chk and self._index_class is btree_index.BTreeGraphIndex: 
1178
 
            index._leaf_factory = btree_index._gcchk_factory
1179
 
        return index
 
1754
        return self._index_class(transport, index_name, index_size,
 
1755
                                 unlimited_cache=unlimited_cache)
1180
1756
 
1181
1757
    def _max_pack_count(self, total_revisions):
1182
1758
        """Return the maximum number of packs to use for total revisions.
1211
1787
        """
1212
1788
        for pack in packs:
1213
1789
            try:
1214
 
                pack.pack_transport.move(pack.file_name(),
 
1790
                pack.pack_transport.rename(pack.file_name(),
1215
1791
                    '../obsolete_packs/' + pack.file_name())
1216
1792
            except (errors.PathError, errors.TransportError), e:
1217
1793
                # TODO: Should these be warnings or mutters?
1225
1801
                suffixes.append('.cix')
1226
1802
            for suffix in suffixes:
1227
1803
                try:
1228
 
                    self._index_transport.move(pack.name + suffix,
 
1804
                    self._index_transport.rename(pack.name + suffix,
1229
1805
                        '../obsolete_packs/' + pack.name + suffix)
1230
1806
                except (errors.PathError, errors.TransportError), e:
1231
1807
                    mutter("couldn't rename obsolete index, skipping it:\n%s"
1368
1944
                    # disk index because the set values are the same, unless
1369
1945
                    # the only index shows up as deleted by the set difference
1370
1946
                    # - which it may. Until there is a specific test for this,
1371
 
                    # assume it's broken. RBC 20071017.
 
1947
                    # assume its broken. RBC 20071017.
1372
1948
                    self._remove_pack_from_memory(self.get_pack_by_name(name))
1373
1949
                    self._names[name] = sizes
1374
1950
                    self.get_pack_by_name(name)
1439
2015
        """
1440
2016
        # The ensure_loaded call is to handle the case where the first call
1441
2017
        # made involving the collection was to reload_pack_names, where we 
1442
 
        # don't have a view of disk contents. It's a bit of a bandaid, and
1443
 
        # causes two reads of pack-names, but it's a rare corner case not
1444
 
        # struck with regular push/pull etc.
 
2018
        # don't have a view of disk contents. Its a bit of a bandaid, and
 
2019
        # causes two reads of pack-names, but its a rare corner case not struck
 
2020
        # with regular push/pull etc.
1445
2021
        first_read = self.ensure_loaded()
1446
2022
        if first_read:
1447
2023
            return True
1466
2042
            raise
1467
2043
        raise errors.RetryAutopack(self.repo, False, sys.exc_info())
1468
2044
 
1469
 
    def _restart_pack_operations(self):
1470
 
        """Reload the pack names list, and restart the autopack code."""
1471
 
        if not self.reload_pack_names():
1472
 
            # Re-raise the original exception, because something went missing
1473
 
            # and a restart didn't find it
1474
 
            raise
1475
 
        raise RetryPackOperations(self.repo, False, sys.exc_info())
1476
 
 
1477
2045
    def _clear_obsolete_packs(self, preserve=None):
1478
2046
        """Delete everything from the obsolete-packs directory.
1479
2047
 
1627
2195
            self._resume_pack(token)
1628
2196
 
1629
2197
 
1630
 
class PackRepository(MetaDirVersionedFileRepository):
 
2198
class KnitPackRepository(KnitRepository):
1631
2199
    """Repository with knit objects stored inside pack containers.
1632
2200
 
1633
2201
    The layering for a KnitPackRepository is:
1636
2204
    ===================================================
1637
2205
    Tuple based apis below, string based, and key based apis above
1638
2206
    ---------------------------------------------------
1639
 
    VersionedFiles
 
2207
    KnitVersionedFiles
1640
2208
      Provides .texts, .revisions etc
1641
2209
      This adapts the N-tuple keys to physical knit records which only have a
1642
2210
      single string identifier (for historical reasons), which in older formats
1652
2220
 
1653
2221
    """
1654
2222
 
1655
 
    # These attributes are inherited from the Repository base class. Setting
1656
 
    # them to None ensures that if the constructor is changed to not initialize
1657
 
    # them, or a subclass fails to call the constructor, that an error will
1658
 
    # occur rather than the system working but generating incorrect data.
1659
 
    _commit_builder_class = None
1660
 
    _serializer = None
1661
 
 
1662
2223
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
1663
2224
        _serializer):
1664
 
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
1665
 
        self._commit_builder_class = _commit_builder_class
1666
 
        self._serializer = _serializer
 
2225
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
2226
            _commit_builder_class, _serializer)
 
2227
        index_transport = self._transport.clone('indices')
 
2228
        self._pack_collection = RepositoryPackCollection(self, self._transport,
 
2229
            index_transport,
 
2230
            self._transport.clone('upload'),
 
2231
            self._transport.clone('packs'),
 
2232
            _format.index_builder_class,
 
2233
            _format.index_class,
 
2234
            use_chk_index=self._format.supports_chks,
 
2235
            )
 
2236
        self.inventories = KnitVersionedFiles(
 
2237
            _KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
 
2238
                add_callback=self._pack_collection.inventory_index.add_callback,
 
2239
                deltas=True, parents=True, is_locked=self.is_locked),
 
2240
            data_access=self._pack_collection.inventory_index.data_access,
 
2241
            max_delta_chain=200)
 
2242
        self.revisions = KnitVersionedFiles(
 
2243
            _KnitGraphIndex(self._pack_collection.revision_index.combined_index,
 
2244
                add_callback=self._pack_collection.revision_index.add_callback,
 
2245
                deltas=False, parents=True, is_locked=self.is_locked,
 
2246
                track_external_parent_refs=True),
 
2247
            data_access=self._pack_collection.revision_index.data_access,
 
2248
            max_delta_chain=0)
 
2249
        self.signatures = KnitVersionedFiles(
 
2250
            _KnitGraphIndex(self._pack_collection.signature_index.combined_index,
 
2251
                add_callback=self._pack_collection.signature_index.add_callback,
 
2252
                deltas=False, parents=False, is_locked=self.is_locked),
 
2253
            data_access=self._pack_collection.signature_index.data_access,
 
2254
            max_delta_chain=0)
 
2255
        self.texts = KnitVersionedFiles(
 
2256
            _KnitGraphIndex(self._pack_collection.text_index.combined_index,
 
2257
                add_callback=self._pack_collection.text_index.add_callback,
 
2258
                deltas=True, parents=True, is_locked=self.is_locked),
 
2259
            data_access=self._pack_collection.text_index.data_access,
 
2260
            max_delta_chain=200)
 
2261
        if _format.supports_chks:
 
2262
            # No graph, no compression:- references from chks are between
 
2263
            # different objects not temporal versions of the same; and without
 
2264
            # some sort of temporal structure knit compression will just fail.
 
2265
            self.chk_bytes = KnitVersionedFiles(
 
2266
                _KnitGraphIndex(self._pack_collection.chk_index.combined_index,
 
2267
                    add_callback=self._pack_collection.chk_index.add_callback,
 
2268
                    deltas=False, parents=False, is_locked=self.is_locked),
 
2269
                data_access=self._pack_collection.chk_index.data_access,
 
2270
                max_delta_chain=0)
 
2271
        else:
 
2272
            self.chk_bytes = None
 
2273
        # True when the repository object is 'write locked' (as opposed to the
 
2274
        # physical lock only taken out around changes to the pack-names list.)
 
2275
        # Another way to represent this would be a decorator around the control
 
2276
        # files object that presents logical locks as physical ones - if this
 
2277
        # gets ugly consider that alternative design. RBC 20071011
 
2278
        self._write_lock_count = 0
 
2279
        self._transaction = None
 
2280
        # for tests
 
2281
        self._reconcile_does_inventory_gc = True
1667
2282
        self._reconcile_fixes_text_parents = True
1668
 
        if self._format.supports_external_lookups:
1669
 
            self._unstacked_provider = graph.CachingParentsProvider(
1670
 
                self._make_parents_provider_unstacked())
1671
 
        else:
1672
 
            self._unstacked_provider = graph.CachingParentsProvider(self)
1673
 
        self._unstacked_provider.disable_cache()
 
2283
        self._reconcile_backsup_inventory = False
1674
2284
 
1675
 
    @needs_read_lock
1676
 
    def _all_revision_ids(self):
1677
 
        """See Repository.all_revision_ids()."""
1678
 
        return [key[0] for key in self.revisions.keys()]
 
2285
    def _warn_if_deprecated(self, branch=None):
 
2286
        # This class isn't deprecated, but one sub-format is
 
2287
        if isinstance(self._format, RepositoryFormatKnitPack5RichRootBroken):
 
2288
            super(KnitPackRepository, self)._warn_if_deprecated(branch)
1679
2289
 
1680
2290
    def _abort_write_group(self):
1681
2291
        self.revisions._index._key_dependencies.clear()
1682
2292
        self._pack_collection._abort_write_group()
1683
2293
 
 
2294
    def _get_source(self, to_format):
 
2295
        if to_format.network_name() == self._format.network_name():
 
2296
            return KnitPackStreamSource(self, to_format)
 
2297
        return super(KnitPackRepository, self)._get_source(to_format)
 
2298
 
1684
2299
    def _make_parents_provider(self):
1685
 
        if not self._format.supports_external_lookups:
1686
 
            return self._unstacked_provider
1687
 
        return graph.StackedParentsProvider(_LazyListJoin(
1688
 
            [self._unstacked_provider], self._fallback_repositories))
 
2300
        return graph.CachingParentsProvider(self)
1689
2301
 
1690
2302
    def _refresh_data(self):
1691
2303
        if not self.is_locked():
1692
2304
            return
1693
2305
        self._pack_collection.reload_pack_names()
1694
 
        self._unstacked_provider.disable_cache()
1695
 
        self._unstacked_provider.enable_cache()
1696
2306
 
1697
2307
    def _start_write_group(self):
1698
2308
        self._pack_collection._start_write_group()
1700
2310
    def _commit_write_group(self):
1701
2311
        hint = self._pack_collection._commit_write_group()
1702
2312
        self.revisions._index._key_dependencies.clear()
1703
 
        # The commit may have added keys that were previously cached as
1704
 
        # missing, so reset the cache.
1705
 
        self._unstacked_provider.disable_cache()
1706
 
        self._unstacked_provider.enable_cache()
1707
2313
        return hint
1708
2314
 
1709
2315
    def suspend_write_group(self):
1750
2356
            if 'relock' in debug.debug_flags and self._prev_lock == 'w':
1751
2357
                note('%r was write locked again', self)
1752
2358
            self._prev_lock = 'w'
1753
 
            self._unstacked_provider.enable_cache()
1754
2359
            for repo in self._fallback_repositories:
1755
2360
                # Writes don't affect fallback repos
1756
2361
                repo.lock_read()
1771
2376
            if 'relock' in debug.debug_flags and self._prev_lock == 'r':
1772
2377
                note('%r was read locked again', self)
1773
2378
            self._prev_lock = 'r'
1774
 
            self._unstacked_provider.enable_cache()
1775
2379
            for repo in self._fallback_repositories:
1776
2380
                repo.lock_read()
1777
2381
            self._refresh_data()
1803
2407
        return reconciler
1804
2408
 
1805
2409
    def _reconcile_pack(self, collection, packs, extension, revs, pb):
1806
 
        raise NotImplementedError(self._reconcile_pack)
 
2410
        packer = ReconcilePacker(collection, packs, extension, revs)
 
2411
        return packer.pack(pb)
1807
2412
 
1808
2413
    @only_raises(errors.LockNotHeld, errors.LockBroken)
1809
2414
    def unlock(self):
1810
2415
        if self._write_lock_count == 1 and self._write_group is not None:
1811
2416
            self.abort_write_group()
1812
 
            self._unstacked_provider.disable_cache()
1813
2417
            self._transaction = None
1814
2418
            self._write_lock_count = 0
1815
2419
            raise errors.BzrError(
1825
2429
            self.control_files.unlock()
1826
2430
 
1827
2431
        if not self.is_locked():
1828
 
            self._unstacked_provider.disable_cache()
1829
2432
            for repo in self._fallback_repositories:
1830
2433
                repo.unlock()
1831
2434
 
1832
2435
 
1833
 
class RepositoryFormatPack(MetaDirVersionedFileRepositoryFormat):
 
2436
class KnitPackStreamSource(StreamSource):
 
2437
    """A StreamSource used to transfer data between same-format KnitPack repos.
 
2438
 
 
2439
    This source assumes:
 
2440
        1) Same serialization format for all objects
 
2441
        2) Same root information
 
2442
        3) XML format inventories
 
2443
        4) Atomic inserts (so we can stream inventory texts before text
 
2444
           content)
 
2445
        5) No chk_bytes
 
2446
    """
 
2447
 
 
2448
    def __init__(self, from_repository, to_format):
 
2449
        super(KnitPackStreamSource, self).__init__(from_repository, to_format)
 
2450
        self._text_keys = None
 
2451
        self._text_fetch_order = 'unordered'
 
2452
 
 
2453
    def _get_filtered_inv_stream(self, revision_ids):
 
2454
        from_repo = self.from_repository
 
2455
        parent_ids = from_repo._find_parent_ids_of_revisions(revision_ids)
 
2456
        parent_keys = [(p,) for p in parent_ids]
 
2457
        find_text_keys = from_repo._find_text_key_references_from_xml_inventory_lines
 
2458
        parent_text_keys = set(find_text_keys(
 
2459
            from_repo._inventory_xml_lines_for_keys(parent_keys)))
 
2460
        content_text_keys = set()
 
2461
        knit = KnitVersionedFiles(None, None)
 
2462
        factory = KnitPlainFactory()
 
2463
        def find_text_keys_from_content(record):
 
2464
            if record.storage_kind not in ('knit-delta-gz', 'knit-ft-gz'):
 
2465
                raise ValueError("Unknown content storage kind for"
 
2466
                    " inventory text: %s" % (record.storage_kind,))
 
2467
            # It's a knit record, it has a _raw_record field (even if it was
 
2468
            # reconstituted from a network stream).
 
2469
            raw_data = record._raw_record
 
2470
            # read the entire thing
 
2471
            revision_id = record.key[-1]
 
2472
            content, _ = knit._parse_record(revision_id, raw_data)
 
2473
            if record.storage_kind == 'knit-delta-gz':
 
2474
                line_iterator = factory.get_linedelta_content(content)
 
2475
            elif record.storage_kind == 'knit-ft-gz':
 
2476
                line_iterator = factory.get_fulltext_content(content)
 
2477
            content_text_keys.update(find_text_keys(
 
2478
                [(line, revision_id) for line in line_iterator]))
 
2479
        revision_keys = [(r,) for r in revision_ids]
 
2480
        def _filtered_inv_stream():
 
2481
            source_vf = from_repo.inventories
 
2482
            stream = source_vf.get_record_stream(revision_keys,
 
2483
                                                 'unordered', False)
 
2484
            for record in stream:
 
2485
                if record.storage_kind == 'absent':
 
2486
                    raise errors.NoSuchRevision(from_repo, record.key)
 
2487
                find_text_keys_from_content(record)
 
2488
                yield record
 
2489
            self._text_keys = content_text_keys - parent_text_keys
 
2490
        return ('inventories', _filtered_inv_stream())
 
2491
 
 
2492
    def _get_text_stream(self):
 
2493
        # Note: We know we don't have to handle adding root keys, because both
 
2494
        # the source and target are the identical network name.
 
2495
        text_stream = self.from_repository.texts.get_record_stream(
 
2496
                        self._text_keys, self._text_fetch_order, False)
 
2497
        return ('texts', text_stream)
 
2498
 
 
2499
    def get_stream(self, search):
 
2500
        revision_ids = search.get_keys()
 
2501
        for stream_info in self._fetch_revision_texts(revision_ids):
 
2502
            yield stream_info
 
2503
        self._revision_keys = [(rev_id,) for rev_id in revision_ids]
 
2504
        yield self._get_filtered_inv_stream(revision_ids)
 
2505
        yield self._get_text_stream()
 
2506
 
 
2507
 
 
2508
 
 
2509
class RepositoryFormatPack(MetaDirRepositoryFormat):
1834
2510
    """Format logic for pack structured repositories.
1835
2511
 
1836
2512
    This repository format has:
1866
2542
    index_class = None
1867
2543
    _fetch_uses_deltas = True
1868
2544
    fast_deltas = False
1869
 
    supports_funky_characters = True
1870
 
    revision_graph_can_have_wrong_parents = True
1871
2545
 
1872
2546
    def initialize(self, a_bzrdir, shared=False):
1873
2547
        """Create a pack based repository.
1910
2584
                              _serializer=self._serializer)
1911
2585
 
1912
2586
 
1913
 
class RetryPackOperations(errors.RetryWithNewPacks):
1914
 
    """Raised when we are packing and we find a missing file.
1915
 
 
1916
 
    Meant as a signaling exception, to tell the RepositoryPackCollection.pack
1917
 
    code it should try again.
1918
 
    """
1919
 
 
1920
 
    internal_error = True
1921
 
 
1922
 
    _fmt = ("Pack files have changed, reload and try pack again."
1923
 
            " context: %(context)s %(orig_error)s")
1924
 
 
1925
 
 
1926
 
class _DirectPackAccess(object):
1927
 
    """Access to data in one or more packs with less translation."""
1928
 
 
1929
 
    def __init__(self, index_to_packs, reload_func=None, flush_func=None):
1930
 
        """Create a _DirectPackAccess object.
1931
 
 
1932
 
        :param index_to_packs: A dict mapping index objects to the transport
1933
 
            and file names for obtaining data.
1934
 
        :param reload_func: A function to call if we determine that the pack
1935
 
            files have moved and we need to reload our caches. See
1936
 
            bzrlib.repo_fmt.pack_repo.AggregateIndex for more details.
1937
 
        """
1938
 
        self._container_writer = None
1939
 
        self._write_index = None
1940
 
        self._indices = index_to_packs
1941
 
        self._reload_func = reload_func
1942
 
        self._flush_func = flush_func
1943
 
 
1944
 
    def add_raw_records(self, key_sizes, raw_data):
1945
 
        """Add raw knit bytes to a storage area.
1946
 
 
1947
 
        The data is spooled to the container writer in one bytes-record per
1948
 
        raw data item.
1949
 
 
1950
 
        :param sizes: An iterable of tuples containing the key and size of each
1951
 
            raw data segment.
1952
 
        :param raw_data: A bytestring containing the data.
1953
 
        :return: A list of memos to retrieve the record later. Each memo is an
1954
 
            opaque index memo. For _DirectPackAccess the memo is (index, pos,
1955
 
            length), where the index field is the write_index object supplied
1956
 
            to the PackAccess object.
1957
 
        """
1958
 
        if type(raw_data) is not str:
1959
 
            raise AssertionError(
1960
 
                'data must be plain bytes was %s' % type(raw_data))
1961
 
        result = []
1962
 
        offset = 0
1963
 
        for key, size in key_sizes:
1964
 
            p_offset, p_length = self._container_writer.add_bytes_record(
1965
 
                raw_data[offset:offset+size], [])
1966
 
            offset += size
1967
 
            result.append((self._write_index, p_offset, p_length))
1968
 
        return result
1969
 
 
1970
 
    def flush(self):
1971
 
        """Flush pending writes on this access object.
1972
 
 
1973
 
        This will flush any buffered writes to a NewPack.
1974
 
        """
1975
 
        if self._flush_func is not None:
1976
 
            self._flush_func()
1977
 
 
1978
 
    def get_raw_records(self, memos_for_retrieval):
1979
 
        """Get the raw bytes for a records.
1980
 
 
1981
 
        :param memos_for_retrieval: An iterable containing the (index, pos,
1982
 
            length) memo for retrieving the bytes. The Pack access method
1983
 
            looks up the pack to use for a given record in its index_to_pack
1984
 
            map.
1985
 
        :return: An iterator over the bytes of the records.
1986
 
        """
1987
 
        # first pass, group into same-index requests
1988
 
        request_lists = []
1989
 
        current_index = None
1990
 
        for (index, offset, length) in memos_for_retrieval:
1991
 
            if current_index == index:
1992
 
                current_list.append((offset, length))
1993
 
            else:
1994
 
                if current_index is not None:
1995
 
                    request_lists.append((current_index, current_list))
1996
 
                current_index = index
1997
 
                current_list = [(offset, length)]
1998
 
        # handle the last entry
1999
 
        if current_index is not None:
2000
 
            request_lists.append((current_index, current_list))
2001
 
        for index, offsets in request_lists:
2002
 
            try:
2003
 
                transport, path = self._indices[index]
2004
 
            except KeyError:
2005
 
                # A KeyError here indicates that someone has triggered an index
2006
 
                # reload, and this index has gone missing, we need to start
2007
 
                # over.
2008
 
                if self._reload_func is None:
2009
 
                    # If we don't have a _reload_func there is nothing that can
2010
 
                    # be done
2011
 
                    raise
2012
 
                raise errors.RetryWithNewPacks(index,
2013
 
                                               reload_occurred=True,
2014
 
                                               exc_info=sys.exc_info())
2015
 
            try:
2016
 
                reader = pack.make_readv_reader(transport, path, offsets)
2017
 
                for names, read_func in reader.iter_records():
2018
 
                    yield read_func(None)
2019
 
            except errors.NoSuchFile:
2020
 
                # A NoSuchFile error indicates that a pack file has gone
2021
 
                # missing on disk, we need to trigger a reload, and start over.
2022
 
                if self._reload_func is None:
2023
 
                    raise
2024
 
                raise errors.RetryWithNewPacks(transport.abspath(path),
2025
 
                                               reload_occurred=False,
2026
 
                                               exc_info=sys.exc_info())
2027
 
 
2028
 
    def set_writer(self, writer, index, transport_packname):
2029
 
        """Set a writer to use for adding data."""
2030
 
        if index is not None:
2031
 
            self._indices[index] = transport_packname
2032
 
        self._container_writer = writer
2033
 
        self._write_index = index
2034
 
 
2035
 
    def reload_or_raise(self, retry_exc):
2036
 
        """Try calling the reload function, or re-raise the original exception.
2037
 
 
2038
 
        This should be called after _DirectPackAccess raises a
2039
 
        RetryWithNewPacks exception. This function will handle the common logic
2040
 
        of determining when the error is fatal versus being temporary.
2041
 
        It will also make sure that the original exception is raised, rather
2042
 
        than the RetryWithNewPacks exception.
2043
 
 
2044
 
        If this function returns, then the calling function should retry
2045
 
        whatever operation was being performed. Otherwise an exception will
2046
 
        be raised.
2047
 
 
2048
 
        :param retry_exc: A RetryWithNewPacks exception.
2049
 
        """
2050
 
        is_error = False
2051
 
        if self._reload_func is None:
2052
 
            is_error = True
2053
 
        elif not self._reload_func():
2054
 
            # The reload claimed that nothing changed
2055
 
            if not retry_exc.reload_occurred:
2056
 
                # If there wasn't an earlier reload, then we really were
2057
 
                # expecting to find changes. We didn't find them, so this is a
2058
 
                # hard error
2059
 
                is_error = True
2060
 
        if is_error:
2061
 
            exc_class, exc_value, exc_traceback = retry_exc.exc_info
2062
 
            raise exc_class, exc_value, exc_traceback
2063
 
 
2064
 
 
 
2587
class RepositoryFormatKnitPack1(RepositoryFormatPack):
 
2588
    """A no-subtrees parameterized Pack repository.
 
2589
 
 
2590
    This format was introduced in 0.92.
 
2591
    """
 
2592
 
 
2593
    repository_class = KnitPackRepository
 
2594
    _commit_builder_class = PackCommitBuilder
 
2595
    @property
 
2596
    def _serializer(self):
 
2597
        return xml5.serializer_v5
 
2598
    # What index classes to use
 
2599
    index_builder_class = InMemoryGraphIndex
 
2600
    index_class = GraphIndex
 
2601
 
 
2602
    def _get_matching_bzrdir(self):
 
2603
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
 
2604
 
 
2605
    def _ignore_setting_bzrdir(self, format):
 
2606
        pass
 
2607
 
 
2608
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2609
 
 
2610
    def get_format_string(self):
 
2611
        """See RepositoryFormat.get_format_string()."""
 
2612
        return "Bazaar pack repository format 1 (needs bzr 0.92)\n"
 
2613
 
 
2614
    def get_format_description(self):
 
2615
        """See RepositoryFormat.get_format_description()."""
 
2616
        return "Packs containing knits without subtree support"
 
2617
 
 
2618
 
 
2619
class RepositoryFormatKnitPack3(RepositoryFormatPack):
 
2620
    """A subtrees parameterized Pack repository.
 
2621
 
 
2622
    This repository format uses the xml7 serializer to get:
 
2623
     - support for recording full info about the tree root
 
2624
     - support for recording tree-references
 
2625
 
 
2626
    This format was introduced in 0.92.
 
2627
    """
 
2628
 
 
2629
    repository_class = KnitPackRepository
 
2630
    _commit_builder_class = PackRootCommitBuilder
 
2631
    rich_root_data = True
 
2632
    experimental = True
 
2633
    supports_tree_reference = True
 
2634
    @property
 
2635
    def _serializer(self):
 
2636
        return xml7.serializer_v7
 
2637
    # What index classes to use
 
2638
    index_builder_class = InMemoryGraphIndex
 
2639
    index_class = GraphIndex
 
2640
 
 
2641
    def _get_matching_bzrdir(self):
 
2642
        return bzrdir.format_registry.make_bzrdir(
 
2643
            'pack-0.92-subtree')
 
2644
 
 
2645
    def _ignore_setting_bzrdir(self, format):
 
2646
        pass
 
2647
 
 
2648
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2649
 
 
2650
    def get_format_string(self):
 
2651
        """See RepositoryFormat.get_format_string()."""
 
2652
        return "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n"
 
2653
 
 
2654
    def get_format_description(self):
 
2655
        """See RepositoryFormat.get_format_description()."""
 
2656
        return "Packs containing knits with subtree support\n"
 
2657
 
 
2658
 
 
2659
class RepositoryFormatKnitPack4(RepositoryFormatPack):
 
2660
    """A rich-root, no subtrees parameterized Pack repository.
 
2661
 
 
2662
    This repository format uses the xml6 serializer to get:
 
2663
     - support for recording full info about the tree root
 
2664
 
 
2665
    This format was introduced in 1.0.
 
2666
    """
 
2667
 
 
2668
    repository_class = KnitPackRepository
 
2669
    _commit_builder_class = PackRootCommitBuilder
 
2670
    rich_root_data = True
 
2671
    supports_tree_reference = False
 
2672
    @property
 
2673
    def _serializer(self):
 
2674
        return xml6.serializer_v6
 
2675
    # What index classes to use
 
2676
    index_builder_class = InMemoryGraphIndex
 
2677
    index_class = GraphIndex
 
2678
 
 
2679
    def _get_matching_bzrdir(self):
 
2680
        return bzrdir.format_registry.make_bzrdir(
 
2681
            'rich-root-pack')
 
2682
 
 
2683
    def _ignore_setting_bzrdir(self, format):
 
2684
        pass
 
2685
 
 
2686
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2687
 
 
2688
    def get_format_string(self):
 
2689
        """See RepositoryFormat.get_format_string()."""
 
2690
        return ("Bazaar pack repository format 1 with rich root"
 
2691
                " (needs bzr 1.0)\n")
 
2692
 
 
2693
    def get_format_description(self):
 
2694
        """See RepositoryFormat.get_format_description()."""
 
2695
        return "Packs containing knits with rich root support\n"
 
2696
 
 
2697
 
 
2698
class RepositoryFormatKnitPack5(RepositoryFormatPack):
 
2699
    """Repository that supports external references to allow stacking.
 
2700
 
 
2701
    New in release 1.6.
 
2702
 
 
2703
    Supports external lookups, which results in non-truncated ghosts after
 
2704
    reconcile compared to pack-0.92 formats.
 
2705
    """
 
2706
 
 
2707
    repository_class = KnitPackRepository
 
2708
    _commit_builder_class = PackCommitBuilder
 
2709
    supports_external_lookups = True
 
2710
    # What index classes to use
 
2711
    index_builder_class = InMemoryGraphIndex
 
2712
    index_class = GraphIndex
 
2713
 
 
2714
    @property
 
2715
    def _serializer(self):
 
2716
        return xml5.serializer_v5
 
2717
 
 
2718
    def _get_matching_bzrdir(self):
 
2719
        return bzrdir.format_registry.make_bzrdir('1.6')
 
2720
 
 
2721
    def _ignore_setting_bzrdir(self, format):
 
2722
        pass
 
2723
 
 
2724
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2725
 
 
2726
    def get_format_string(self):
 
2727
        """See RepositoryFormat.get_format_string()."""
 
2728
        return "Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
 
2729
 
 
2730
    def get_format_description(self):
 
2731
        """See RepositoryFormat.get_format_description()."""
 
2732
        return "Packs 5 (adds stacking support, requires bzr 1.6)"
 
2733
 
 
2734
 
 
2735
class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack):
 
2736
    """A repository with rich roots and stacking.
 
2737
 
 
2738
    New in release 1.6.1.
 
2739
 
 
2740
    Supports stacking on other repositories, allowing data to be accessed
 
2741
    without being stored locally.
 
2742
    """
 
2743
 
 
2744
    repository_class = KnitPackRepository
 
2745
    _commit_builder_class = PackRootCommitBuilder
 
2746
    rich_root_data = True
 
2747
    supports_tree_reference = False # no subtrees
 
2748
    supports_external_lookups = True
 
2749
    # What index classes to use
 
2750
    index_builder_class = InMemoryGraphIndex
 
2751
    index_class = GraphIndex
 
2752
 
 
2753
    @property
 
2754
    def _serializer(self):
 
2755
        return xml6.serializer_v6
 
2756
 
 
2757
    def _get_matching_bzrdir(self):
 
2758
        return bzrdir.format_registry.make_bzrdir(
 
2759
            '1.6.1-rich-root')
 
2760
 
 
2761
    def _ignore_setting_bzrdir(self, format):
 
2762
        pass
 
2763
 
 
2764
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2765
 
 
2766
    def get_format_string(self):
 
2767
        """See RepositoryFormat.get_format_string()."""
 
2768
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
 
2769
 
 
2770
    def get_format_description(self):
 
2771
        return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
 
2772
 
 
2773
 
 
2774
class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack):
 
2775
    """A repository with rich roots and external references.
 
2776
 
 
2777
    New in release 1.6.
 
2778
 
 
2779
    Supports external lookups, which results in non-truncated ghosts after
 
2780
    reconcile compared to pack-0.92 formats.
 
2781
 
 
2782
    This format was deprecated because the serializer it uses accidentally
 
2783
    supported subtrees, when the format was not intended to. This meant that
 
2784
    someone could accidentally fetch from an incorrect repository.
 
2785
    """
 
2786
 
 
2787
    repository_class = KnitPackRepository
 
2788
    _commit_builder_class = PackRootCommitBuilder
 
2789
    rich_root_data = True
 
2790
    supports_tree_reference = False # no subtrees
 
2791
 
 
2792
    supports_external_lookups = True
 
2793
    # What index classes to use
 
2794
    index_builder_class = InMemoryGraphIndex
 
2795
    index_class = GraphIndex
 
2796
 
 
2797
    @property
 
2798
    def _serializer(self):
 
2799
        return xml7.serializer_v7
 
2800
 
 
2801
    def _get_matching_bzrdir(self):
 
2802
        matching = bzrdir.format_registry.make_bzrdir(
 
2803
            '1.6.1-rich-root')
 
2804
        matching.repository_format = self
 
2805
        return matching
 
2806
 
 
2807
    def _ignore_setting_bzrdir(self, format):
 
2808
        pass
 
2809
 
 
2810
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2811
 
 
2812
    def get_format_string(self):
 
2813
        """See RepositoryFormat.get_format_string()."""
 
2814
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n"
 
2815
 
 
2816
    def get_format_description(self):
 
2817
        return ("Packs 5 rich-root (adds stacking support, requires bzr 1.6)"
 
2818
                " (deprecated)")
 
2819
 
 
2820
 
 
2821
class RepositoryFormatKnitPack6(RepositoryFormatPack):
 
2822
    """A repository with stacking and btree indexes,
 
2823
    without rich roots or subtrees.
 
2824
 
 
2825
    This is equivalent to pack-1.6 with B+Tree indices.
 
2826
    """
 
2827
 
 
2828
    repository_class = KnitPackRepository
 
2829
    _commit_builder_class = PackCommitBuilder
 
2830
    supports_external_lookups = True
 
2831
    # What index classes to use
 
2832
    index_builder_class = BTreeBuilder
 
2833
    index_class = BTreeGraphIndex
 
2834
 
 
2835
    @property
 
2836
    def _serializer(self):
 
2837
        return xml5.serializer_v5
 
2838
 
 
2839
    def _get_matching_bzrdir(self):
 
2840
        return bzrdir.format_registry.make_bzrdir('1.9')
 
2841
 
 
2842
    def _ignore_setting_bzrdir(self, format):
 
2843
        pass
 
2844
 
 
2845
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2846
 
 
2847
    def get_format_string(self):
 
2848
        """See RepositoryFormat.get_format_string()."""
 
2849
        return "Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n"
 
2850
 
 
2851
    def get_format_description(self):
 
2852
        """See RepositoryFormat.get_format_description()."""
 
2853
        return "Packs 6 (uses btree indexes, requires bzr 1.9)"
 
2854
 
 
2855
 
 
2856
class RepositoryFormatKnitPack6RichRoot(RepositoryFormatPack):
 
2857
    """A repository with rich roots, no subtrees, stacking and btree indexes.
 
2858
 
 
2859
    1.6-rich-root with B+Tree indices.
 
2860
    """
 
2861
 
 
2862
    repository_class = KnitPackRepository
 
2863
    _commit_builder_class = PackRootCommitBuilder
 
2864
    rich_root_data = True
 
2865
    supports_tree_reference = False # no subtrees
 
2866
    supports_external_lookups = True
 
2867
    # What index classes to use
 
2868
    index_builder_class = BTreeBuilder
 
2869
    index_class = BTreeGraphIndex
 
2870
 
 
2871
    @property
 
2872
    def _serializer(self):
 
2873
        return xml6.serializer_v6
 
2874
 
 
2875
    def _get_matching_bzrdir(self):
 
2876
        return bzrdir.format_registry.make_bzrdir(
 
2877
            '1.9-rich-root')
 
2878
 
 
2879
    def _ignore_setting_bzrdir(self, format):
 
2880
        pass
 
2881
 
 
2882
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2883
 
 
2884
    def get_format_string(self):
 
2885
        """See RepositoryFormat.get_format_string()."""
 
2886
        return "Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n"
 
2887
 
 
2888
    def get_format_description(self):
 
2889
        return "Packs 6 rich-root (uses btree indexes, requires bzr 1.9)"
 
2890
 
 
2891
 
 
2892
class RepositoryFormatPackDevelopment2Subtree(RepositoryFormatPack):
 
2893
    """A subtrees development repository.
 
2894
 
 
2895
    This format should be retained until the second release after bzr 1.7.
 
2896
 
 
2897
    1.6.1-subtree[as it might have been] with B+Tree indices.
 
2898
 
 
2899
    This is [now] retained until we have a CHK based subtree format in
 
2900
    development.
 
2901
    """
 
2902
 
 
2903
    repository_class = KnitPackRepository
 
2904
    _commit_builder_class = PackRootCommitBuilder
 
2905
    rich_root_data = True
 
2906
    experimental = True
 
2907
    supports_tree_reference = True
 
2908
    supports_external_lookups = True
 
2909
    # What index classes to use
 
2910
    index_builder_class = BTreeBuilder
 
2911
    index_class = BTreeGraphIndex
 
2912
 
 
2913
    @property
 
2914
    def _serializer(self):
 
2915
        return xml7.serializer_v7
 
2916
 
 
2917
    def _get_matching_bzrdir(self):
 
2918
        return bzrdir.format_registry.make_bzrdir(
 
2919
            'development-subtree')
 
2920
 
 
2921
    def _ignore_setting_bzrdir(self, format):
 
2922
        pass
 
2923
 
 
2924
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2925
 
 
2926
    def get_format_string(self):
 
2927
        """See RepositoryFormat.get_format_string()."""
 
2928
        return ("Bazaar development format 2 with subtree support "
 
2929
            "(needs bzr.dev from before 1.8)\n")
 
2930
 
 
2931
    def get_format_description(self):
 
2932
        """See RepositoryFormat.get_format_description()."""
 
2933
        return ("Development repository format, currently the same as "
 
2934
            "1.6.1-subtree with B+Tree indices.\n")
2065
2935