827
732
        Sets self._text_filter appropriately.
 
829
 
        # select inventory keys
 
830
 
        inv_keys = self._revision_keys # currently the same keyspace, and note that
 
831
 
        # querying for keys here could introduce a bug where an inventory item
 
832
 
        # is missed, so do not change it to query separately without cross
 
833
 
        # checking like the text key check below.
 
834
 
        inventory_index_map, inventory_indices = self._pack_map_and_index_list(
 
836
 
        inv_nodes = self._index_contents(inventory_indices, inv_keys)
 
837
 
        # copy inventory keys and adjust values
 
838
 
        # XXX: Should be a helper function to allow different inv representation
 
840
 
        self.pb.update("Copying inventory texts", 2)
 
841
 
        total_items, readv_group_iter = self._least_readv_node_readv(inv_nodes)
 
842
 
        # Only grab the output lines if we will be processing them
 
843
 
        output_lines = bool(self.revision_ids)
 
844
 
        inv_lines = self._copy_nodes_graph(inventory_index_map,
 
845
 
            self.new_pack._writer, self.new_pack.inventory_index,
 
846
 
            readv_group_iter, total_items, output_lines=output_lines)
 
847
 
        if self.revision_ids:
 
848
 
            self._process_inventory_lines(inv_lines)
 
850
 
            # eat the iterator to cause it to execute.
 
852
 
            self._text_filter = None
 
853
 
        if 'pack' in debug.debug_flags:
 
854
 
            mutter('%s: create_pack: inventories copied: %s%s %d items t+%6.3fs',
 
855
 
                time.ctime(), self._pack_collection._upload_transport.base,
 
856
 
                self.new_pack.random_name,
 
857
 
                self.new_pack.inventory_index.key_count(),
 
858
 
                time.time() - self.new_pack.start_time)
 
 
734
        raise NotImplementedError(self._copy_inventory_texts)
 
860
736
    def _copy_text_texts(self):
 
862
 
        text_index_map, text_nodes = self._get_text_nodes()
 
863
 
        if self._text_filter is not None:
 
864
 
            # We could return the keys copied as part of the return value from
 
865
 
            # _copy_nodes_graph but this doesn't work all that well with the
 
866
 
            # need to get line output too, so we check separately, and as we're
 
867
 
            # going to buffer everything anyway, we check beforehand, which
 
868
 
            # saves reading knit data over the wire when we know there are
 
870
 
            text_nodes = set(text_nodes)
 
871
 
            present_text_keys = set(_node[1] for _node in text_nodes)
 
872
 
            missing_text_keys = set(self._text_filter) - present_text_keys
 
873
 
            if missing_text_keys:
 
874
 
                # TODO: raise a specific error that can handle many missing
 
876
 
                mutter("missing keys during fetch: %r", missing_text_keys)
 
877
 
                a_missing_key = missing_text_keys.pop()
 
878
 
                raise errors.RevisionNotPresent(a_missing_key[1],
 
880
 
        # copy text keys and adjust values
 
881
 
        self.pb.update("Copying content texts", 3)
 
882
 
        total_items, readv_group_iter = self._least_readv_node_readv(text_nodes)
 
883
 
        list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
 
884
 
            self.new_pack.text_index, readv_group_iter, total_items))
 
885
 
        self._log_copied_texts()
 
 
737
        raise NotImplementedError(self._copy_text_texts)
 
887
739
    def _create_pack_from_packs(self):
 
888
 
        self.pb.update("Opening pack", 0, 5)
 
889
 
        self.new_pack = self.open_pack()
 
890
 
        new_pack = self.new_pack
 
891
 
        # buffer data - we won't be reading-back during the pack creation and
 
892
 
        # this makes a significant difference on sftp pushes.
 
893
 
        new_pack.set_write_cache_size(1024*1024)
 
894
 
        if 'pack' in debug.debug_flags:
 
895
 
            plain_pack_list = ['%s%s' % (a_pack.pack_transport.base, a_pack.name)
 
896
 
                for a_pack in self.packs]
 
897
 
            if self.revision_ids is not None:
 
898
 
                rev_count = len(self.revision_ids)
 
901
 
            mutter('%s: create_pack: creating pack from source packs: '
 
902
 
                '%s%s %s revisions wanted %s t=0',
 
903
 
                time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
 
904
 
                plain_pack_list, rev_count)
 
905
 
        self._copy_revision_texts()
 
906
 
        self._copy_inventory_texts()
 
907
 
        self._copy_text_texts()
 
908
 
        # select signature keys
 
909
 
        signature_filter = self._revision_keys # same keyspace
 
910
 
        signature_index_map, signature_indices = self._pack_map_and_index_list(
 
912
 
        signature_nodes = self._index_contents(signature_indices,
 
914
 
        # copy signature keys and adjust values
 
915
 
        self.pb.update("Copying signature texts", 4)
 
916
 
        self._copy_nodes(signature_nodes, signature_index_map, new_pack._writer,
 
917
 
            new_pack.signature_index)
 
918
 
        if 'pack' in debug.debug_flags:
 
919
 
            mutter('%s: create_pack: revision signatures copied: %s%s %d items t+%6.3fs',
 
920
 
                time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
 
921
 
                new_pack.signature_index.key_count(),
 
922
 
                time.time() - new_pack.start_time)
 
924
 
        # NB XXX: how to check CHK references are present? perhaps by yielding
 
925
 
        # the items? How should that interact with stacked repos?
 
926
 
        if new_pack.chk_index is not None:
 
928
 
            if 'pack' in debug.debug_flags:
 
929
 
                mutter('%s: create_pack: chk content copied: %s%s %d items t+%6.3fs',
 
930
 
                    time.ctime(), self._pack_collection._upload_transport.base,
 
931
 
                    new_pack.random_name,
 
932
 
                    new_pack.chk_index.key_count(),
 
933
 
                    time.time() - new_pack.start_time)
 
934
 
        new_pack._check_references()
 
935
 
        if not self._use_pack(new_pack):
 
938
 
        self.pb.update("Finishing pack", 5)
 
940
 
        self._pack_collection.allocate(new_pack)
 
943
 
    def _copy_chks(self, refs=None):
 
944
 
        # XXX: Todo, recursive follow-pointers facility when fetching some
 
946
 
        chk_index_map, chk_indices = self._pack_map_and_index_list(
 
948
 
        chk_nodes = self._index_contents(chk_indices, refs)
 
950
 
        # TODO: This isn't strictly tasteful as we are accessing some private
 
951
 
        #       variables (_serializer). Perhaps a better way would be to have
 
952
 
        #       Repository._deserialise_chk_node()
 
953
 
        search_key_func = chk_map.search_key_registry.get(
 
954
 
            self._pack_collection.repo._serializer.search_key_name)
 
955
 
        def accumlate_refs(lines):
 
956
 
            # XXX: move to a generic location
 
958
 
            bytes = ''.join(lines)
 
959
 
            node = chk_map._deserialise(bytes, ("unknown",), search_key_func)
 
960
 
            new_refs.update(node.refs())
 
961
 
        self._copy_nodes(chk_nodes, chk_index_map, self.new_pack._writer,
 
962
 
            self.new_pack.chk_index, output_lines=accumlate_refs)
 
965
 
    def _copy_nodes(self, nodes, index_map, writer, write_index,
 
967
 
        """Copy knit nodes between packs with no graph references.
 
969
 
        :param output_lines: Output full texts of copied items.
 
971
 
        pb = ui.ui_factory.nested_progress_bar()
 
973
 
            return self._do_copy_nodes(nodes, index_map, writer,
 
974
 
                write_index, pb, output_lines=output_lines)
 
978
 
    def _do_copy_nodes(self, nodes, index_map, writer, write_index, pb,
 
980
 
        # for record verification
 
981
 
        knit = KnitVersionedFiles(None, None)
 
982
 
        # plan a readv on each source pack:
 
984
 
        nodes = sorted(nodes)
 
985
 
        # how to map this into knit.py - or knit.py into this?
 
986
 
        # we don't want the typical knit logic, we want grouping by pack
 
987
 
        # at this point - perhaps a helper library for the following code
 
988
 
        # duplication points?
 
990
 
        for index, key, value in nodes:
 
991
 
            if index not in request_groups:
 
992
 
                request_groups[index] = []
 
993
 
            request_groups[index].append((key, value))
 
995
 
        pb.update("Copied record", record_index, len(nodes))
 
996
 
        for index, items in request_groups.iteritems():
 
997
 
            pack_readv_requests = []
 
998
 
            for key, value in items:
 
999
 
                # ---- KnitGraphIndex.get_position
 
1000
 
                bits = value[1:].split(' ')
 
1001
 
                offset, length = int(bits[0]), int(bits[1])
 
1002
 
                pack_readv_requests.append((offset, length, (key, value[0])))
 
1003
 
            # linear scan up the pack
 
1004
 
            pack_readv_requests.sort()
 
1006
 
            pack_obj = index_map[index]
 
1007
 
            transport, path = pack_obj.access_tuple()
 
1009
 
                reader = pack.make_readv_reader(transport, path,
 
1010
 
                    [offset[0:2] for offset in pack_readv_requests])
 
1011
 
            except errors.NoSuchFile:
 
1012
 
                if self._reload_func is not None:
 
1015
 
            for (names, read_func), (_1, _2, (key, eol_flag)) in \
 
1016
 
                izip(reader.iter_records(), pack_readv_requests):
 
1017
 
                raw_data = read_func(None)
 
1018
 
                # check the header only
 
1019
 
                if output_lines is not None:
 
1020
 
                    output_lines(knit._parse_record(key[-1], raw_data)[0])
 
1022
 
                    df, _ = knit._parse_record_header(key, raw_data)
 
1024
 
                pos, size = writer.add_bytes_record(raw_data, names)
 
1025
 
                write_index.add_node(key, eol_flag + "%d %d" % (pos, size))
 
1026
 
                pb.update("Copied record", record_index)
 
1029
 
    def _copy_nodes_graph(self, index_map, writer, write_index,
 
1030
 
        readv_group_iter, total_items, output_lines=False):
 
1031
 
        """Copy knit nodes between packs.
 
1033
 
        :param output_lines: Return lines present in the copied data as
 
1034
 
            an iterator of line,version_id.
 
1036
 
        pb = ui.ui_factory.nested_progress_bar()
 
1038
 
            for result in self._do_copy_nodes_graph(index_map, writer,
 
1039
 
                write_index, output_lines, pb, readv_group_iter, total_items):
 
1042
 
            # Python 2.4 does not permit try:finally: in a generator.
 
1048
 
    def _do_copy_nodes_graph(self, index_map, writer, write_index,
 
1049
 
        output_lines, pb, readv_group_iter, total_items):
 
1050
 
        # for record verification
 
1051
 
        knit = KnitVersionedFiles(None, None)
 
1052
 
        # for line extraction when requested (inventories only)
 
1054
 
            factory = KnitPlainFactory()
 
1056
 
        pb.update("Copied record", record_index, total_items)
 
1057
 
        for index, readv_vector, node_vector in readv_group_iter:
 
1059
 
            pack_obj = index_map[index]
 
1060
 
            transport, path = pack_obj.access_tuple()
 
1062
 
                reader = pack.make_readv_reader(transport, path, readv_vector)
 
1063
 
            except errors.NoSuchFile:
 
1064
 
                if self._reload_func is not None:
 
1067
 
            for (names, read_func), (key, eol_flag, references) in \
 
1068
 
                izip(reader.iter_records(), node_vector):
 
1069
 
                raw_data = read_func(None)
 
1071
 
                    # read the entire thing
 
1072
 
                    content, _ = knit._parse_record(key[-1], raw_data)
 
1073
 
                    if len(references[-1]) == 0:
 
1074
 
                        line_iterator = factory.get_fulltext_content(content)
 
1076
 
                        line_iterator = factory.get_linedelta_content(content)
 
1077
 
                    for line in line_iterator:
 
1080
 
                    # check the header only
 
1081
 
                    df, _ = knit._parse_record_header(key, raw_data)
 
1083
 
                pos, size = writer.add_bytes_record(raw_data, names)
 
1084
 
                write_index.add_node(key, eol_flag + "%d %d" % (pos, size), references)
 
1085
 
                pb.update("Copied record", record_index)
 
1088
 
    def _get_text_nodes(self):
 
1089
 
        text_index_map, text_indices = self._pack_map_and_index_list(
 
1091
 
        return text_index_map, self._index_contents(text_indices,
 
1094
 
    def _least_readv_node_readv(self, nodes):
 
1095
 
        """Generate request groups for nodes using the least readv's.
 
1097
 
        :param nodes: An iterable of graph index nodes.
 
1098
 
        :return: Total node count and an iterator of the data needed to perform
 
1099
 
            readvs to obtain the data for nodes. Each item yielded by the
 
1100
 
            iterator is a tuple with:
 
1101
 
            index, readv_vector, node_vector. readv_vector is a list ready to
 
1102
 
            hand to the transport readv method, and node_vector is a list of
 
1103
 
            (key, eol_flag, references) for the node retrieved by the
 
1104
 
            matching readv_vector.
 
1106
 
        # group by pack so we do one readv per pack
 
1107
 
        nodes = sorted(nodes)
 
1110
 
        for index, key, value, references in nodes:
 
1111
 
            if index not in request_groups:
 
1112
 
                request_groups[index] = []
 
1113
 
            request_groups[index].append((key, value, references))
 
1115
 
        for index, items in request_groups.iteritems():
 
1116
 
            pack_readv_requests = []
 
1117
 
            for key, value, references in items:
 
1118
 
                # ---- KnitGraphIndex.get_position
 
1119
 
                bits = value[1:].split(' ')
 
1120
 
                offset, length = int(bits[0]), int(bits[1])
 
1121
 
                pack_readv_requests.append(
 
1122
 
                    ((offset, length), (key, value[0], references)))
 
1123
 
            # linear scan up the pack to maximum range combining.
 
1124
 
            pack_readv_requests.sort()
 
1125
 
            # split out the readv and the node data.
 
1126
 
            pack_readv = [readv for readv, node in pack_readv_requests]
 
1127
 
            node_vector = [node for readv, node in pack_readv_requests]
 
1128
 
            result.append((index, pack_readv, node_vector))
 
1129
 
        return total, result
 
 
740
        raise NotImplementedError(self._create_pack_from_packs)
 
1131
742
    def _log_copied_texts(self):
 
1132
743
        if 'pack' in debug.debug_flags:
 
 
1164
756
        return new_pack.data_inserted()
 
1167
 
class OptimisingPacker(Packer):
 
1168
 
    """A packer which spends more time to create better disk layouts."""
 
1170
 
    def _revision_node_readv(self, revision_nodes):
 
1171
 
        """Return the total revisions and the readv's to issue.
 
1173
 
        This sort places revisions in topological order with the ancestors
 
1176
 
        :param revision_nodes: The revision index contents for the packs being
 
1177
 
            incorporated into the new pack.
 
1178
 
        :return: As per _least_readv_node_readv.
 
1180
 
        # build an ancestors dict
 
1183
 
        for index, key, value, references in revision_nodes:
 
1184
 
            ancestors[key] = references[0]
 
1185
 
            by_key[key] = (index, value, references)
 
1186
 
        order = tsort.topo_sort(ancestors)
 
1188
 
        # Single IO is pathological, but it will work as a starting point.
 
1190
 
        for key in reversed(order):
 
1191
 
            index, value, references = by_key[key]
 
1192
 
            # ---- KnitGraphIndex.get_position
 
1193
 
            bits = value[1:].split(' ')
 
1194
 
            offset, length = int(bits[0]), int(bits[1])
 
1196
 
                (index, [(offset, length)], [(key, value[0], references)]))
 
1197
 
        # TODO: combine requests in the same index that are in ascending order.
 
1198
 
        return total, requests
 
1200
 
    def open_pack(self):
 
1201
 
        """Open a pack for the pack we are creating."""
 
1202
 
        new_pack = super(OptimisingPacker, self).open_pack()
 
1203
 
        # Turn on the optimization flags for all the index builders.
 
1204
 
        new_pack.revision_index.set_optimize(for_size=True)
 
1205
 
        new_pack.inventory_index.set_optimize(for_size=True)
 
1206
 
        new_pack.text_index.set_optimize(for_size=True)
 
1207
 
        new_pack.signature_index.set_optimize(for_size=True)
 
1211
 
class ReconcilePacker(Packer):
 
1212
 
    """A packer which regenerates indices etc as it copies.
 
1214
 
    This is used by ``bzr reconcile`` to cause parent text pointers to be
 
1218
 
    def _extra_init(self):
 
1219
 
        self._data_changed = False
 
1221
 
    def _process_inventory_lines(self, inv_lines):
 
1222
 
        """Generate a text key reference map rather for reconciling with."""
 
1223
 
        repo = self._pack_collection.repo
 
1224
 
        refs = repo._find_text_key_references_from_xml_inventory_lines(
 
1226
 
        self._text_refs = refs
 
1227
 
        # during reconcile we:
 
1228
 
        #  - convert unreferenced texts to full texts
 
1229
 
        #  - correct texts which reference a text not copied to be full texts
 
1230
 
        #  - copy all others as-is but with corrected parents.
 
1231
 
        #  - so at this point we don't know enough to decide what becomes a full
 
1233
 
        self._text_filter = None
 
1235
 
    def _copy_text_texts(self):
 
1236
 
        """generate what texts we should have and then copy."""
 
1237
 
        self.pb.update("Copying content texts", 3)
 
1238
 
        # we have three major tasks here:
 
1239
 
        # 1) generate the ideal index
 
1240
 
        repo = self._pack_collection.repo
 
1241
 
        ancestors = dict([(key[0], tuple(ref[0] for ref in refs[0])) for
 
1242
 
            _1, key, _2, refs in
 
1243
 
            self.new_pack.revision_index.iter_all_entries()])
 
1244
 
        ideal_index = repo._generate_text_key_index(self._text_refs, ancestors)
 
1245
 
        # 2) generate a text_nodes list that contains all the deltas that can
 
1246
 
        #    be used as-is, with corrected parents.
 
1249
 
        discarded_nodes = []
 
1250
 
        NULL_REVISION = _mod_revision.NULL_REVISION
 
1251
 
        text_index_map, text_nodes = self._get_text_nodes()
 
1252
 
        for node in text_nodes:
 
1258
 
                ideal_parents = tuple(ideal_index[node[1]])
 
1260
 
                discarded_nodes.append(node)
 
1261
 
                self._data_changed = True
 
1263
 
                if ideal_parents == (NULL_REVISION,):
 
1265
 
                if ideal_parents == node[3][0]:
 
1267
 
                    ok_nodes.append(node)
 
1268
 
                elif ideal_parents[0:1] == node[3][0][0:1]:
 
1269
 
                    # the left most parent is the same, or there are no parents
 
1270
 
                    # today. Either way, we can preserve the representation as
 
1271
 
                    # long as we change the refs to be inserted.
 
1272
 
                    self._data_changed = True
 
1273
 
                    ok_nodes.append((node[0], node[1], node[2],
 
1274
 
                        (ideal_parents, node[3][1])))
 
1275
 
                    self._data_changed = True
 
1277
 
                    # Reinsert this text completely
 
1278
 
                    bad_texts.append((node[1], ideal_parents))
 
1279
 
                    self._data_changed = True
 
1280
 
        # we're finished with some data.
 
1283
 
        # 3) bulk copy the ok data
 
1284
 
        total_items, readv_group_iter = self._least_readv_node_readv(ok_nodes)
 
1285
 
        list(self._copy_nodes_graph(text_index_map, self.new_pack._writer,
 
1286
 
            self.new_pack.text_index, readv_group_iter, total_items))
 
1287
 
        # 4) adhoc copy all the other texts.
 
1288
 
        # We have to topologically insert all texts otherwise we can fail to
 
1289
 
        # reconcile when parts of a single delta chain are preserved intact,
 
1290
 
        # and other parts are not. E.g. Discarded->d1->d2->d3. d1 will be
 
1291
 
        # reinserted, and if d3 has incorrect parents it will also be
 
1292
 
        # reinserted. If we insert d3 first, d2 is present (as it was bulk
 
1293
 
        # copied), so we will try to delta, but d2 is not currently able to be
 
1294
 
        # extracted because it's basis d1 is not present. Topologically sorting
 
1295
 
        # addresses this. The following generates a sort for all the texts that
 
1296
 
        # are being inserted without having to reference the entire text key
 
1297
 
        # space (we only topo sort the revisions, which is smaller).
 
1298
 
        topo_order = tsort.topo_sort(ancestors)
 
1299
 
        rev_order = dict(zip(topo_order, range(len(topo_order))))
 
1300
 
        bad_texts.sort(key=lambda key:rev_order.get(key[0][1], 0))
 
1301
 
        transaction = repo.get_transaction()
 
1302
 
        file_id_index = GraphIndexPrefixAdapter(
 
1303
 
            self.new_pack.text_index,
 
1305
 
            add_nodes_callback=self.new_pack.text_index.add_nodes)
 
1306
 
        data_access = _DirectPackAccess(
 
1307
 
                {self.new_pack.text_index:self.new_pack.access_tuple()})
 
1308
 
        data_access.set_writer(self.new_pack._writer, self.new_pack.text_index,
 
1309
 
            self.new_pack.access_tuple())
 
1310
 
        output_texts = KnitVersionedFiles(
 
1311
 
            _KnitGraphIndex(self.new_pack.text_index,
 
1312
 
                add_callback=self.new_pack.text_index.add_nodes,
 
1313
 
                deltas=True, parents=True, is_locked=repo.is_locked),
 
1314
 
            data_access=data_access, max_delta_chain=200)
 
1315
 
        for key, parent_keys in bad_texts:
 
1316
 
            # We refer to the new pack to delta data being output.
 
1317
 
            # A possible improvement would be to catch errors on short reads
 
1318
 
            # and only flush then.
 
1319
 
            self.new_pack.flush()
 
1321
 
            for parent_key in parent_keys:
 
1322
 
                if parent_key[0] != key[0]:
 
1323
 
                    # Graph parents must match the fileid
 
1324
 
                    raise errors.BzrError('Mismatched key parent %r:%r' %
 
1326
 
                parents.append(parent_key[1])
 
1327
 
            text_lines = osutils.split_lines(repo.texts.get_record_stream(
 
1328
 
                [key], 'unordered', True).next().get_bytes_as('fulltext'))
 
1329
 
            output_texts.add_lines(key, parent_keys, text_lines,
 
1330
 
                random_id=True, check_content=False)
 
1331
 
        # 5) check that nothing inserted has a reference outside the keyspace.
 
1332
 
        missing_text_keys = self.new_pack.text_index._external_references()
 
1333
 
        if missing_text_keys:
 
1334
 
            raise errors.BzrCheckError('Reference to missing compression parents %r'
 
1335
 
                % (missing_text_keys,))
 
1336
 
        self._log_copied_texts()
 
1338
 
    def _use_pack(self, new_pack):
 
1339
 
        """Override _use_pack to check for reconcile having changed content."""
 
1340
 
        # XXX: we might be better checking this at the copy time.
 
1341
 
        original_inventory_keys = set()
 
1342
 
        inv_index = self._pack_collection.inventory_index.combined_index
 
1343
 
        for entry in inv_index.iter_all_entries():
 
1344
 
            original_inventory_keys.add(entry[1])
 
1345
 
        new_inventory_keys = set()
 
1346
 
        for entry in new_pack.inventory_index.iter_all_entries():
 
1347
 
            new_inventory_keys.add(entry[1])
 
1348
 
        if new_inventory_keys != original_inventory_keys:
 
1349
 
            self._data_changed = True
 
1350
 
        return new_pack.data_inserted() and self._data_changed
 
1353
759
class RepositoryPackCollection(object):
 
1354
760
    """Management of packs within a repository.
 
1356
762
    :ivar _names: map of {pack_name: (index_size,)}
 
1359
 
    pack_factory = NewPack
 
1360
 
    resumed_pack_factory = ResumedPack
 
 
766
    resumed_pack_factory = None
 
 
767
    normal_packer_class = None
 
 
768
    optimising_packer_class = None
 
1362
770
    def __init__(self, repo, transport, index_transport, upload_transport,
 
1363
771
                 pack_transport, index_builder_class, index_class,
 
 
 
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
 
2221
1662
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
 
2223
 
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
 
2224
 
            _commit_builder_class, _serializer)
 
2225
 
        index_transport = self._transport.clone('indices')
 
2226
 
        self._pack_collection = RepositoryPackCollection(self, self._transport,
 
2228
 
            self._transport.clone('upload'),
 
2229
 
            self._transport.clone('packs'),
 
2230
 
            _format.index_builder_class,
 
2231
 
            _format.index_class,
 
2232
 
            use_chk_index=self._format.supports_chks,
 
2234
 
        self.inventories = KnitVersionedFiles(
 
2235
 
            _KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
 
2236
 
                add_callback=self._pack_collection.inventory_index.add_callback,
 
2237
 
                deltas=True, parents=True, is_locked=self.is_locked),
 
2238
 
            data_access=self._pack_collection.inventory_index.data_access,
 
2239
 
            max_delta_chain=200)
 
2240
 
        self.revisions = KnitVersionedFiles(
 
2241
 
            _KnitGraphIndex(self._pack_collection.revision_index.combined_index,
 
2242
 
                add_callback=self._pack_collection.revision_index.add_callback,
 
2243
 
                deltas=False, parents=True, is_locked=self.is_locked,
 
2244
 
                track_external_parent_refs=True),
 
2245
 
            data_access=self._pack_collection.revision_index.data_access,
 
2247
 
        self.signatures = KnitVersionedFiles(
 
2248
 
            _KnitGraphIndex(self._pack_collection.signature_index.combined_index,
 
2249
 
                add_callback=self._pack_collection.signature_index.add_callback,
 
2250
 
                deltas=False, parents=False, is_locked=self.is_locked),
 
2251
 
            data_access=self._pack_collection.signature_index.data_access,
 
2253
 
        self.texts = KnitVersionedFiles(
 
2254
 
            _KnitGraphIndex(self._pack_collection.text_index.combined_index,
 
2255
 
                add_callback=self._pack_collection.text_index.add_callback,
 
2256
 
                deltas=True, parents=True, is_locked=self.is_locked),
 
2257
 
            data_access=self._pack_collection.text_index.data_access,
 
2258
 
            max_delta_chain=200)
 
2259
 
        if _format.supports_chks:
 
2260
 
            # No graph, no compression:- references from chks are between
 
2261
 
            # different objects not temporal versions of the same; and without
 
2262
 
            # some sort of temporal structure knit compression will just fail.
 
2263
 
            self.chk_bytes = KnitVersionedFiles(
 
2264
 
                _KnitGraphIndex(self._pack_collection.chk_index.combined_index,
 
2265
 
                    add_callback=self._pack_collection.chk_index.add_callback,
 
2266
 
                    deltas=False, parents=False, is_locked=self.is_locked),
 
2267
 
                data_access=self._pack_collection.chk_index.data_access,
 
2270
 
            self.chk_bytes = None
 
2271
 
        # True when the repository object is 'write locked' (as opposed to the
 
2272
 
        # physical lock only taken out around changes to the pack-names list.)
 
2273
 
        # Another way to represent this would be a decorator around the control
 
2274
 
        # files object that presents logical locks as physical ones - if this
 
2275
 
        # gets ugly consider that alternative design. RBC 20071011
 
2276
 
        self._write_lock_count = 0
 
2277
 
        self._transaction = None
 
2279
 
        self._reconcile_does_inventory_gc = True
 
 
1664
        MetaDirRepository.__init__(self, _format, a_bzrdir, control_files)
 
 
1665
        self._commit_builder_class = _commit_builder_class
 
 
1666
        self._serializer = _serializer
 
2280
1667
        self._reconcile_fixes_text_parents = True
 
2281
 
        self._reconcile_backsup_inventory = False
 
 
1668
        if self._format.supports_external_lookups:
 
 
1669
            self._unstacked_provider = graph.CachingParentsProvider(
 
 
1670
                self._make_parents_provider_unstacked())
 
 
1672
            self._unstacked_provider = graph.CachingParentsProvider(self)
 
 
1673
        self._unstacked_provider.disable_cache()
 
2283
 
    def _warn_if_deprecated(self, branch=None):
 
2284
 
        # This class isn't deprecated, but one sub-format is
 
2285
 
        if isinstance(self._format, RepositoryFormatKnitPack5RichRootBroken):
 
2286
 
            super(KnitPackRepository, self)._warn_if_deprecated(branch)
 
 
1676
    def _all_revision_ids(self):
 
 
1677
        """See Repository.all_revision_ids()."""
 
 
1678
        return [key[0] for key in self.revisions.keys()]
 
2288
1680
    def _abort_write_group(self):
 
2289
1681
        self.revisions._index._key_dependencies.clear()
 
2290
1682
        self._pack_collection._abort_write_group()
 
2292
 
    def _get_source(self, to_format):
 
2293
 
        if to_format.network_name() == self._format.network_name():
 
2294
 
            return KnitPackStreamSource(self, to_format)
 
2295
 
        return super(KnitPackRepository, self)._get_source(to_format)
 
2297
1684
    def _make_parents_provider(self):
 
2298
 
        return graph.CachingParentsProvider(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
1690
    def _refresh_data(self):
 
2301
1691
        if not self.is_locked():
 
2303
1693
        self._pack_collection.reload_pack_names()
 
 
1694
        self._unstacked_provider.disable_cache()
 
 
1695
        self._unstacked_provider.enable_cache()
 
2305
1697
    def _start_write_group(self):
 
2306
1698
        self._pack_collection._start_write_group()
 
 
2417
1825
            self.control_files.unlock()
 
2419
1827
        if not self.is_locked():
 
 
1828
            self._unstacked_provider.disable_cache()
 
2420
1829
            for repo in self._fallback_repositories:
 
2424
 
class KnitPackStreamSource(StreamSource):
 
2425
 
    """A StreamSource used to transfer data between same-format KnitPack repos.
 
2427
 
    This source assumes:
 
2428
 
        1) Same serialization format for all objects
 
2429
 
        2) Same root information
 
2430
 
        3) XML format inventories
 
2431
 
        4) Atomic inserts (so we can stream inventory texts before text
 
2436
 
    def __init__(self, from_repository, to_format):
 
2437
 
        super(KnitPackStreamSource, self).__init__(from_repository, to_format)
 
2438
 
        self._text_keys = None
 
2439
 
        self._text_fetch_order = 'unordered'
 
2441
 
    def _get_filtered_inv_stream(self, revision_ids):
 
2442
 
        from_repo = self.from_repository
 
2443
 
        parent_ids = from_repo._find_parent_ids_of_revisions(revision_ids)
 
2444
 
        parent_keys = [(p,) for p in parent_ids]
 
2445
 
        find_text_keys = from_repo._find_text_key_references_from_xml_inventory_lines
 
2446
 
        parent_text_keys = set(find_text_keys(
 
2447
 
            from_repo._inventory_xml_lines_for_keys(parent_keys)))
 
2448
 
        content_text_keys = set()
 
2449
 
        knit = KnitVersionedFiles(None, None)
 
2450
 
        factory = KnitPlainFactory()
 
2451
 
        def find_text_keys_from_content(record):
 
2452
 
            if record.storage_kind not in ('knit-delta-gz', 'knit-ft-gz'):
 
2453
 
                raise ValueError("Unknown content storage kind for"
 
2454
 
                    " inventory text: %s" % (record.storage_kind,))
 
2455
 
            # It's a knit record, it has a _raw_record field (even if it was
 
2456
 
            # reconstituted from a network stream).
 
2457
 
            raw_data = record._raw_record
 
2458
 
            # read the entire thing
 
2459
 
            revision_id = record.key[-1]
 
2460
 
            content, _ = knit._parse_record(revision_id, raw_data)
 
2461
 
            if record.storage_kind == 'knit-delta-gz':
 
2462
 
                line_iterator = factory.get_linedelta_content(content)
 
2463
 
            elif record.storage_kind == 'knit-ft-gz':
 
2464
 
                line_iterator = factory.get_fulltext_content(content)
 
2465
 
            content_text_keys.update(find_text_keys(
 
2466
 
                [(line, revision_id) for line in line_iterator]))
 
2467
 
        revision_keys = [(r,) for r in revision_ids]
 
2468
 
        def _filtered_inv_stream():
 
2469
 
            source_vf = from_repo.inventories
 
2470
 
            stream = source_vf.get_record_stream(revision_keys,
 
2472
 
            for record in stream:
 
2473
 
                if record.storage_kind == 'absent':
 
2474
 
                    raise errors.NoSuchRevision(from_repo, record.key)
 
2475
 
                find_text_keys_from_content(record)
 
2477
 
            self._text_keys = content_text_keys - parent_text_keys
 
2478
 
        return ('inventories', _filtered_inv_stream())
 
2480
 
    def _get_text_stream(self):
 
2481
 
        # Note: We know we don't have to handle adding root keys, because both
 
2482
 
        # the source and target are the identical network name.
 
2483
 
        text_stream = self.from_repository.texts.get_record_stream(
 
2484
 
                        self._text_keys, self._text_fetch_order, False)
 
2485
 
        return ('texts', text_stream)
 
2487
 
    def get_stream(self, search):
 
2488
 
        revision_ids = search.get_keys()
 
2489
 
        for stream_info in self._fetch_revision_texts(revision_ids):
 
2491
 
        self._revision_keys = [(rev_id,) for rev_id in revision_ids]
 
2492
 
        yield self._get_filtered_inv_stream(revision_ids)
 
2493
 
        yield self._get_text_stream()
 
2497
 
class RepositoryFormatPack(MetaDirRepositoryFormat):
 
 
1833
class RepositoryFormatPack(MetaDirVersionedFileRepositoryFormat):
 
2498
1834
    """Format logic for pack structured repositories.
 
2500
1836
    This repository format has:
 
 
2572
1910
                              _serializer=self._serializer)
 
2575
 
class RepositoryFormatKnitPack1(RepositoryFormatPack):
 
2576
 
    """A no-subtrees parameterized Pack repository.
 
2578
 
    This format was introduced in 0.92.
 
2581
 
    repository_class = KnitPackRepository
 
2582
 
    _commit_builder_class = PackCommitBuilder
 
2584
 
    def _serializer(self):
 
2585
 
        return xml5.serializer_v5
 
2586
 
    # What index classes to use
 
2587
 
    index_builder_class = InMemoryGraphIndex
 
2588
 
    index_class = GraphIndex
 
2590
 
    def _get_matching_bzrdir(self):
 
2591
 
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
 
2593
 
    def _ignore_setting_bzrdir(self, format):
 
2596
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2598
 
    def get_format_string(self):
 
2599
 
        """See RepositoryFormat.get_format_string()."""
 
2600
 
        return "Bazaar pack repository format 1 (needs bzr 0.92)\n"
 
2602
 
    def get_format_description(self):
 
2603
 
        """See RepositoryFormat.get_format_description()."""
 
2604
 
        return "Packs containing knits without subtree support"
 
2607
 
class RepositoryFormatKnitPack3(RepositoryFormatPack):
 
2608
 
    """A subtrees parameterized Pack repository.
 
2610
 
    This repository format uses the xml7 serializer to get:
 
2611
 
     - support for recording full info about the tree root
 
2612
 
     - support for recording tree-references
 
2614
 
    This format was introduced in 0.92.
 
2617
 
    repository_class = KnitPackRepository
 
2618
 
    _commit_builder_class = PackRootCommitBuilder
 
2619
 
    rich_root_data = True
 
2621
 
    supports_tree_reference = True
 
2623
 
    def _serializer(self):
 
2624
 
        return xml7.serializer_v7
 
2625
 
    # What index classes to use
 
2626
 
    index_builder_class = InMemoryGraphIndex
 
2627
 
    index_class = GraphIndex
 
2629
 
    def _get_matching_bzrdir(self):
 
2630
 
        return bzrdir.format_registry.make_bzrdir(
 
2631
 
            'pack-0.92-subtree')
 
2633
 
    def _ignore_setting_bzrdir(self, format):
 
2636
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2638
 
    def get_format_string(self):
 
2639
 
        """See RepositoryFormat.get_format_string()."""
 
2640
 
        return "Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n"
 
2642
 
    def get_format_description(self):
 
2643
 
        """See RepositoryFormat.get_format_description()."""
 
2644
 
        return "Packs containing knits with subtree support\n"
 
2647
 
class RepositoryFormatKnitPack4(RepositoryFormatPack):
 
2648
 
    """A rich-root, no subtrees parameterized Pack repository.
 
2650
 
    This repository format uses the xml6 serializer to get:
 
2651
 
     - support for recording full info about the tree root
 
2653
 
    This format was introduced in 1.0.
 
2656
 
    repository_class = KnitPackRepository
 
2657
 
    _commit_builder_class = PackRootCommitBuilder
 
2658
 
    rich_root_data = True
 
2659
 
    supports_tree_reference = False
 
2661
 
    def _serializer(self):
 
2662
 
        return xml6.serializer_v6
 
2663
 
    # What index classes to use
 
2664
 
    index_builder_class = InMemoryGraphIndex
 
2665
 
    index_class = GraphIndex
 
2667
 
    def _get_matching_bzrdir(self):
 
2668
 
        return bzrdir.format_registry.make_bzrdir(
 
2671
 
    def _ignore_setting_bzrdir(self, format):
 
2674
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2676
 
    def get_format_string(self):
 
2677
 
        """See RepositoryFormat.get_format_string()."""
 
2678
 
        return ("Bazaar pack repository format 1 with rich root"
 
2679
 
                " (needs bzr 1.0)\n")
 
2681
 
    def get_format_description(self):
 
2682
 
        """See RepositoryFormat.get_format_description()."""
 
2683
 
        return "Packs containing knits with rich root support\n"
 
2686
 
class RepositoryFormatKnitPack5(RepositoryFormatPack):
 
2687
 
    """Repository that supports external references to allow stacking.
 
2691
 
    Supports external lookups, which results in non-truncated ghosts after
 
2692
 
    reconcile compared to pack-0.92 formats.
 
2695
 
    repository_class = KnitPackRepository
 
2696
 
    _commit_builder_class = PackCommitBuilder
 
2697
 
    supports_external_lookups = True
 
2698
 
    # What index classes to use
 
2699
 
    index_builder_class = InMemoryGraphIndex
 
2700
 
    index_class = GraphIndex
 
2703
 
    def _serializer(self):
 
2704
 
        return xml5.serializer_v5
 
2706
 
    def _get_matching_bzrdir(self):
 
2707
 
        return bzrdir.format_registry.make_bzrdir('1.6')
 
2709
 
    def _ignore_setting_bzrdir(self, format):
 
2712
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2714
 
    def get_format_string(self):
 
2715
 
        """See RepositoryFormat.get_format_string()."""
 
2716
 
        return "Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
 
2718
 
    def get_format_description(self):
 
2719
 
        """See RepositoryFormat.get_format_description()."""
 
2720
 
        return "Packs 5 (adds stacking support, requires bzr 1.6)"
 
2723
 
class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack):
 
2724
 
    """A repository with rich roots and stacking.
 
2726
 
    New in release 1.6.1.
 
2728
 
    Supports stacking on other repositories, allowing data to be accessed
 
2729
 
    without being stored locally.
 
2732
 
    repository_class = KnitPackRepository
 
2733
 
    _commit_builder_class = PackRootCommitBuilder
 
2734
 
    rich_root_data = True
 
2735
 
    supports_tree_reference = False # no subtrees
 
2736
 
    supports_external_lookups = True
 
2737
 
    # What index classes to use
 
2738
 
    index_builder_class = InMemoryGraphIndex
 
2739
 
    index_class = GraphIndex
 
2742
 
    def _serializer(self):
 
2743
 
        return xml6.serializer_v6
 
2745
 
    def _get_matching_bzrdir(self):
 
2746
 
        return bzrdir.format_registry.make_bzrdir(
 
2749
 
    def _ignore_setting_bzrdir(self, format):
 
2752
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2754
 
    def get_format_string(self):
 
2755
 
        """See RepositoryFormat.get_format_string()."""
 
2756
 
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
 
2758
 
    def get_format_description(self):
 
2759
 
        return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
 
2762
 
class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack):
 
2763
 
    """A repository with rich roots and external references.
 
2767
 
    Supports external lookups, which results in non-truncated ghosts after
 
2768
 
    reconcile compared to pack-0.92 formats.
 
2770
 
    This format was deprecated because the serializer it uses accidentally
 
2771
 
    supported subtrees, when the format was not intended to. This meant that
 
2772
 
    someone could accidentally fetch from an incorrect repository.
 
2775
 
    repository_class = KnitPackRepository
 
2776
 
    _commit_builder_class = PackRootCommitBuilder
 
2777
 
    rich_root_data = True
 
2778
 
    supports_tree_reference = False # no subtrees
 
2780
 
    supports_external_lookups = True
 
2781
 
    # What index classes to use
 
2782
 
    index_builder_class = InMemoryGraphIndex
 
2783
 
    index_class = GraphIndex
 
2786
 
    def _serializer(self):
 
2787
 
        return xml7.serializer_v7
 
2789
 
    def _get_matching_bzrdir(self):
 
2790
 
        matching = bzrdir.format_registry.make_bzrdir(
 
2792
 
        matching.repository_format = self
 
2795
 
    def _ignore_setting_bzrdir(self, format):
 
2798
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2800
 
    def get_format_string(self):
 
2801
 
        """See RepositoryFormat.get_format_string()."""
 
2802
 
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n"
 
2804
 
    def get_format_description(self):
 
2805
 
        return ("Packs 5 rich-root (adds stacking support, requires bzr 1.6)"
 
2809
 
class RepositoryFormatKnitPack6(RepositoryFormatPack):
 
2810
 
    """A repository with stacking and btree indexes,
 
2811
 
    without rich roots or subtrees.
 
2813
 
    This is equivalent to pack-1.6 with B+Tree indices.
 
2816
 
    repository_class = KnitPackRepository
 
2817
 
    _commit_builder_class = PackCommitBuilder
 
2818
 
    supports_external_lookups = True
 
2819
 
    # What index classes to use
 
2820
 
    index_builder_class = BTreeBuilder
 
2821
 
    index_class = BTreeGraphIndex
 
2824
 
    def _serializer(self):
 
2825
 
        return xml5.serializer_v5
 
2827
 
    def _get_matching_bzrdir(self):
 
2828
 
        return bzrdir.format_registry.make_bzrdir('1.9')
 
2830
 
    def _ignore_setting_bzrdir(self, format):
 
2833
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2835
 
    def get_format_string(self):
 
2836
 
        """See RepositoryFormat.get_format_string()."""
 
2837
 
        return "Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n"
 
2839
 
    def get_format_description(self):
 
2840
 
        """See RepositoryFormat.get_format_description()."""
 
2841
 
        return "Packs 6 (uses btree indexes, requires bzr 1.9)"
 
2844
 
class RepositoryFormatKnitPack6RichRoot(RepositoryFormatPack):
 
2845
 
    """A repository with rich roots, no subtrees, stacking and btree indexes.
 
2847
 
    1.6-rich-root with B+Tree indices.
 
2850
 
    repository_class = KnitPackRepository
 
2851
 
    _commit_builder_class = PackRootCommitBuilder
 
2852
 
    rich_root_data = True
 
2853
 
    supports_tree_reference = False # no subtrees
 
2854
 
    supports_external_lookups = True
 
2855
 
    # What index classes to use
 
2856
 
    index_builder_class = BTreeBuilder
 
2857
 
    index_class = BTreeGraphIndex
 
2860
 
    def _serializer(self):
 
2861
 
        return xml6.serializer_v6
 
2863
 
    def _get_matching_bzrdir(self):
 
2864
 
        return bzrdir.format_registry.make_bzrdir(
 
2867
 
    def _ignore_setting_bzrdir(self, format):
 
2870
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2872
 
    def get_format_string(self):
 
2873
 
        """See RepositoryFormat.get_format_string()."""
 
2874
 
        return "Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n"
 
2876
 
    def get_format_description(self):
 
2877
 
        return "Packs 6 rich-root (uses btree indexes, requires bzr 1.9)"
 
2880
 
class RepositoryFormatPackDevelopment2Subtree(RepositoryFormatPack):
 
2881
 
    """A subtrees development repository.
 
2883
 
    This format should be retained until the second release after bzr 1.7.
 
2885
 
    1.6.1-subtree[as it might have been] with B+Tree indices.
 
2887
 
    This is [now] retained until we have a CHK based subtree format in
 
2891
 
    repository_class = KnitPackRepository
 
2892
 
    _commit_builder_class = PackRootCommitBuilder
 
2893
 
    rich_root_data = True
 
2895
 
    supports_tree_reference = True
 
2896
 
    supports_external_lookups = True
 
2897
 
    # What index classes to use
 
2898
 
    index_builder_class = BTreeBuilder
 
2899
 
    index_class = BTreeGraphIndex
 
2902
 
    def _serializer(self):
 
2903
 
        return xml7.serializer_v7
 
2905
 
    def _get_matching_bzrdir(self):
 
2906
 
        return bzrdir.format_registry.make_bzrdir(
 
2907
 
            'development-subtree')
 
2909
 
    def _ignore_setting_bzrdir(self, format):
 
2912
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2914
 
    def get_format_string(self):
 
2915
 
        """See RepositoryFormat.get_format_string()."""
 
2916
 
        return ("Bazaar development format 2 with subtree support "
 
2917
 
            "(needs bzr.dev from before 1.8)\n")
 
2919
 
    def get_format_description(self):
 
2920
 
        """See RepositoryFormat.get_format_description()."""
 
2921
 
        return ("Development repository format, currently the same as "
 
2922
 
            "1.6.1-subtree with B+Tree indices.\n")
 
 
1913
class RetryPackOperations(errors.RetryWithNewPacks):
 
 
1914
    """Raised when we are packing and we find a missing file.
 
 
1916
    Meant as a signaling exception, to tell the RepositoryPackCollection.pack
 
 
1917
    code it should try again.
 
 
1920
    internal_error = True
 
 
1922
    _fmt = ("Pack files have changed, reload and try pack again."
 
 
1923
            " context: %(context)s %(orig_error)s")
 
 
1926
class _DirectPackAccess(object):
 
 
1927
    """Access to data in one or more packs with less translation."""
 
 
1929
    def __init__(self, index_to_packs, reload_func=None, flush_func=None):
 
 
1930
        """Create a _DirectPackAccess object.
 
 
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.
 
 
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
 
 
1944
    def add_raw_records(self, key_sizes, raw_data):
 
 
1945
        """Add raw knit bytes to a storage area.
 
 
1947
        The data is spooled to the container writer in one bytes-record per
 
 
1950
        :param sizes: An iterable of tuples containing the key and size of each
 
 
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.
 
 
1958
        if type(raw_data) is not str:
 
 
1959
            raise AssertionError(
 
 
1960
                'data must be plain bytes was %s' % type(raw_data))
 
 
1963
        for key, size in key_sizes:
 
 
1964
            p_offset, p_length = self._container_writer.add_bytes_record(
 
 
1965
                raw_data[offset:offset+size], [])
 
 
1967
            result.append((self._write_index, p_offset, p_length))
 
 
1971
        """Flush pending writes on this access object.
 
 
1973
        This will flush any buffered writes to a NewPack.
 
 
1975
        if self._flush_func is not None:
 
 
1978
    def get_raw_records(self, memos_for_retrieval):
 
 
1979
        """Get the raw bytes for a records.
 
 
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
 
 
1985
        :return: An iterator over the bytes of the records.
 
 
1987
        # first pass, group into same-index requests
 
 
1989
        current_index = None
 
 
1990
        for (index, offset, length) in memos_for_retrieval:
 
 
1991
            if current_index == index:
 
 
1992
                current_list.append((offset, length))
 
 
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:
 
 
2003
                transport, path = self._indices[index]
 
 
2005
                # A KeyError here indicates that someone has triggered an index
 
 
2006
                # reload, and this index has gone missing, we need to start
 
 
2008
                if self._reload_func is None:
 
 
2009
                    # If we don't have a _reload_func there is nothing that can
 
 
2012
                raise errors.RetryWithNewPacks(index,
 
 
2013
                                               reload_occurred=True,
 
 
2014
                                               exc_info=sys.exc_info())
 
 
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:
 
 
2024
                raise errors.RetryWithNewPacks(transport.abspath(path),
 
 
2025
                                               reload_occurred=False,
 
 
2026
                                               exc_info=sys.exc_info())
 
 
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
 
 
2035
    def reload_or_raise(self, retry_exc):
 
 
2036
        """Try calling the reload function, or re-raise the original exception.
 
 
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.
 
 
2044
        If this function returns, then the calling function should retry
 
 
2045
        whatever operation was being performed. Otherwise an exception will
 
 
2048
        :param retry_exc: A RetryWithNewPacks exception.
 
 
2051
        if self._reload_func is None:
 
 
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
 
 
2061
            exc_class, exc_value, exc_traceback = retry_exc.exc_info
 
 
2062
            raise exc_class, exc_value, exc_traceback