~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/pack_repo.py

  • Committer: John Arbash Meinel
  • Date: 2008-10-30 00:55:00 UTC
  • mto: (3815.2.5 prepare-1.9)
  • mto: This revision was merged to the branch mainline in revision 3811.
  • Revision ID: john@arbash-meinel.com-20081030005500-r5cej1cxflqhs3io
Switch so that we are using a simple timestamp as the first action.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
from bzrlib.lazy_import import lazy_import
18
18
lazy_import(globals(), """
19
19
from itertools import izip
20
 
import math
21
 
import md5
22
20
import time
23
21
 
24
22
from bzrlib import (
25
 
        debug,
26
 
        graph,
27
 
        pack,
28
 
        ui,
29
 
        )
 
23
    debug,
 
24
    graph,
 
25
    osutils,
 
26
    pack,
 
27
    transactions,
 
28
    ui,
 
29
    xml5,
 
30
    xml6,
 
31
    xml7,
 
32
    )
30
33
from bzrlib.index import (
 
34
    CombinedGraphIndex,
31
35
    GraphIndex,
32
36
    GraphIndexBuilder,
33
 
    InMemoryGraphIndex,
34
 
    CombinedGraphIndex,
35
37
    GraphIndexPrefixAdapter,
36
 
    )
37
 
from bzrlib.knit import KnitGraphIndex, _PackAccess, _KnitData
38
 
from bzrlib.osutils import rand_chars
39
 
from bzrlib.pack import ContainerWriter
40
 
from bzrlib.store import revision
 
38
    InMemoryGraphIndex,
 
39
    )
 
40
from bzrlib.knit import (
 
41
    KnitPlainFactory,
 
42
    KnitVersionedFiles,
 
43
    _KnitGraphIndex,
 
44
    _DirectPackAccess,
 
45
    )
41
46
from bzrlib import tsort
42
47
""")
43
48
from bzrlib import (
44
49
    bzrdir,
45
50
    errors,
46
 
    knit,
47
51
    lockable_files,
48
52
    lockdir,
49
 
    osutils,
50
53
    symbol_versioning,
51
 
    transactions,
52
 
    xml5,
53
 
    xml6,
54
 
    xml7,
55
54
    )
56
55
 
57
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
56
from bzrlib.decorators import needs_write_lock
 
57
from bzrlib.btree_index import (
 
58
    BTreeGraphIndex,
 
59
    BTreeBuilder,
 
60
    )
 
61
from bzrlib.index import (
 
62
    GraphIndex,
 
63
    InMemoryGraphIndex,
 
64
    )
58
65
from bzrlib.repofmt.knitrepo import KnitRepository
59
66
from bzrlib.repository import (
60
67
    CommitBuilder,
61
 
    MetaDirRepository,
62
68
    MetaDirRepositoryFormat,
63
69
    RepositoryFormat,
64
70
    RootCommitBuilder,
65
71
    )
66
72
import bzrlib.revision as _mod_revision
67
 
from bzrlib.store.revision.knit import KnitRevisionStore
68
 
from bzrlib.store.versioned import VersionedFileStore
69
73
from bzrlib.trace import (
70
74
    mutter,
71
 
    mutter_callsite,
72
 
    note,
73
75
    warning,
74
76
    )
75
77
 
90
92
        self._file_graph = graph.Graph(
91
93
            repository._pack_collection.text_index.combined_index)
92
94
 
93
 
    def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
94
 
        return self.repository._pack_collection._add_text_to_weave(file_id,
95
 
            self._new_revision_id, new_lines, parents, nostore_sha,
96
 
            self.random_revid)
97
 
 
98
95
    def _heads(self, file_id, revision_ids):
99
96
        keys = [(file_id, revision_id) for revision_id in revision_ids]
100
97
        return set([key[1] for key in self._file_graph.heads(keys)])
116
113
        self._file_graph = graph.Graph(
117
114
            repository._pack_collection.text_index.combined_index)
118
115
 
119
 
    def _add_text_to_weave(self, file_id, new_lines, parents, nostore_sha):
120
 
        return self.repository._pack_collection._add_text_to_weave(file_id,
121
 
            self._new_revision_id, new_lines, parents, nostore_sha,
122
 
            self.random_revid)
123
 
 
124
116
    def _heads(self, file_id, revision_ids):
125
117
        keys = [(file_id, revision_id) for revision_id in revision_ids]
126
118
        return set([key[1] for key in self._file_graph.heads(keys)])
145
137
        :param text_index: A GraphIndex for determining what file texts
146
138
            are present in the pack and accessing the locations of their
147
139
            texts/deltas (via (fileid, revisionid) tuples).
148
 
        :param revision_index: A GraphIndex for determining what signatures are
 
140
        :param signature_index: A GraphIndex for determining what signatures are
149
141
            present in the Pack and accessing the locations of their texts.
150
142
        """
151
143
        self.revision_index = revision_index
215
207
 
216
208
    def __repr__(self):
217
209
        return "<bzrlib.repofmt.pack_repo.Pack object at 0x%x, %s, %s" % (
218
 
            id(self), self.transport, self.name)
 
210
            id(self), self.pack_transport, self.name)
219
211
 
220
212
 
221
213
class NewPack(Pack):
231
223
        }
232
224
 
233
225
    def __init__(self, upload_transport, index_transport, pack_transport,
234
 
        upload_suffix='', file_mode=None):
 
226
        upload_suffix='', file_mode=None, index_builder_class=None,
 
227
        index_class=None):
235
228
        """Create a NewPack instance.
236
229
 
237
230
        :param upload_transport: A writable transport for the pack to be
244
237
        :param upload_suffix: An optional suffix to be given to any temporary
245
238
            files created during the pack creation. e.g '.autopack'
246
239
        :param file_mode: An optional file mode to create the new files with.
 
240
        :param index_builder_class: Required keyword parameter - the class of
 
241
            index builder to use.
 
242
        :param index_class: Required keyword parameter - the class of index
 
243
            object to use.
247
244
        """
248
245
        # The relative locations of the packs are constrained, but all are
249
246
        # passed in because the caller has them, so as to avoid object churn.
250
247
        Pack.__init__(self,
251
248
            # Revisions: parents list, no text compression.
252
 
            InMemoryGraphIndex(reference_lists=1),
 
249
            index_builder_class(reference_lists=1),
253
250
            # Inventory: We want to map compression only, but currently the
254
251
            # knit code hasn't been updated enough to understand that, so we
255
252
            # have a regular 2-list index giving parents and compression
256
253
            # source.
257
 
            InMemoryGraphIndex(reference_lists=2),
 
254
            index_builder_class(reference_lists=2),
258
255
            # Texts: compression and per file graph, for all fileids - so two
259
256
            # reference lists and two elements in the key tuple.
260
 
            InMemoryGraphIndex(reference_lists=2, key_elements=2),
 
257
            index_builder_class(reference_lists=2, key_elements=2),
261
258
            # Signatures: Just blobs to store, no compression, no parents
262
259
            # listing.
263
 
            InMemoryGraphIndex(reference_lists=0),
 
260
            index_builder_class(reference_lists=0),
264
261
            )
 
262
        # When we make readonly indices, we need this.
 
263
        self.index_class = index_class
265
264
        # where should the new pack be opened
266
265
        self.upload_transport = upload_transport
267
266
        # where are indices written out to
271
270
        # What file mode to upload the pack and indices with.
272
271
        self._file_mode = file_mode
273
272
        # tracks the content written to the .pack file.
274
 
        self._hash = md5.new()
 
273
        self._hash = osutils.md5()
275
274
        # a four-tuple with the length in bytes of the indices, once the pack
276
275
        # is finalised. (rev, inv, text, sigs)
277
276
        self.index_sizes = None
281
280
        # under creation.
282
281
        self._cache_limit = 0
283
282
        # the temporary pack file name.
284
 
        self.random_name = rand_chars(20) + upload_suffix
 
283
        self.random_name = osutils.rand_chars(20) + upload_suffix
285
284
        # when was this pack started ?
286
285
        self.start_time = time.time()
287
286
        # open an output stream for the data added to the pack.
408
407
 
409
408
    def _replace_index_with_readonly(self, index_type):
410
409
        setattr(self, index_type + '_index',
411
 
            GraphIndex(self.index_transport,
 
410
            self.index_class(self.index_transport,
412
411
                self.index_name(index_type, self.name),
413
412
                self.index_sizes[self.index_offset(index_type)]))
414
413
 
453
452
    # XXX: Probably 'can be written to' could/should be separated from 'acts
454
453
    # like a knit index' -- mbp 20071024
455
454
 
456
 
    def __init__(self):
457
 
        """Create an AggregateIndex."""
 
455
    def __init__(self, reload_func=None):
 
456
        """Create an AggregateIndex.
 
457
 
 
458
        :param reload_func: A function to call if we find we are missing an
 
459
            index. Should have the form reload_func() => True if the list of
 
460
            active pack files has changed.
 
461
        """
 
462
        self._reload_func = reload_func
458
463
        self.index_to_pack = {}
459
 
        self.combined_index = CombinedGraphIndex([])
460
 
        self.knit_access = _PackAccess(self.index_to_pack)
 
464
        self.combined_index = CombinedGraphIndex([], reload_func=reload_func)
 
465
        self.data_access = _DirectPackAccess(self.index_to_pack)
 
466
        self.add_callback = None
461
467
 
462
468
    def replace_indices(self, index_to_pack, indices):
463
469
        """Replace the current mappings with fresh ones.
509
515
        # allow writing: queue writes to a new index
510
516
        self.add_index(index, pack)
511
517
        # Updates the index to packs mapping as a side effect,
512
 
        self.knit_access.set_writer(pack._writer, index, pack.access_tuple())
 
518
        self.data_access.set_writer(pack._writer, index, pack.access_tuple())
513
519
        self.add_callback = index.add_nodes
514
520
 
515
521
    def clear(self):
516
522
        """Reset all the aggregate data to nothing."""
517
 
        self.knit_access.set_writer(None, None, (None, None))
 
523
        self.data_access.set_writer(None, None, (None, None))
518
524
        self.index_to_pack.clear()
519
525
        del self.combined_index._indices[:]
520
526
        self.add_callback = None
530
536
        if (self.add_callback is not None and
531
537
            getattr(index, 'add_nodes', None) == self.add_callback):
532
538
            self.add_callback = None
533
 
            self.knit_access.set_writer(None, None, (None, None))
 
539
            self.data_access.set_writer(None, None, (None, None))
534
540
 
535
541
 
536
542
class Packer(object):
589
595
                return None
590
596
            else:
591
597
                self.revision_ids = frozenset(self.revision_ids)
 
598
                self.revision_keys = frozenset((revid,) for revid in
 
599
                    self.revision_ids)
592
600
        if pb is None:
593
601
            self.pb = ui.ui_factory.nested_progress_bar()
594
602
        else:
604
612
        return NewPack(self._pack_collection._upload_transport,
605
613
            self._pack_collection._index_transport,
606
614
            self._pack_collection._pack_transport, upload_suffix=self.suffix,
607
 
            file_mode=self._pack_collection.repo.bzrdir._get_file_mode())
 
615
            file_mode=self._pack_collection.repo.bzrdir._get_file_mode(),
 
616
            index_builder_class=self._pack_collection._index_builder_class,
 
617
            index_class=self._pack_collection._index_class)
608
618
 
609
619
    def _copy_revision_texts(self):
610
620
        """Copy revision data to the new pack."""
763
773
 
764
774
    def _do_copy_nodes(self, nodes, index_map, writer, write_index, pb):
765
775
        # for record verification
766
 
        knit_data = _KnitData(None)
 
776
        knit = KnitVersionedFiles(None, None)
767
777
        # plan a readv on each source pack:
768
778
        # group by pack
769
779
        nodes = sorted(nodes)
795
805
                izip(reader.iter_records(), pack_readv_requests):
796
806
                raw_data = read_func(None)
797
807
                # check the header only
798
 
                df, _ = knit_data._parse_record_header(key[-1], raw_data)
 
808
                df, _ = knit._parse_record_header(key, raw_data)
799
809
                df.close()
800
810
                pos, size = writer.add_bytes_record(raw_data, names)
801
811
                write_index.add_node(key, eol_flag + "%d %d" % (pos, size))
824
834
    def _do_copy_nodes_graph(self, index_map, writer, write_index,
825
835
        output_lines, pb, readv_group_iter, total_items):
826
836
        # for record verification
827
 
        knit_data = _KnitData(None)
 
837
        knit = KnitVersionedFiles(None, None)
828
838
        # for line extraction when requested (inventories only)
829
839
        if output_lines:
830
 
            factory = knit.KnitPlainFactory()
 
840
            factory = KnitPlainFactory()
831
841
        record_index = 0
832
842
        pb.update("Copied record", record_index, total_items)
833
843
        for index, readv_vector, node_vector in readv_group_iter:
837
847
            for (names, read_func), (key, eol_flag, references) in \
838
848
                izip(reader.iter_records(), node_vector):
839
849
                raw_data = read_func(None)
840
 
                version_id = key[-1]
841
850
                if output_lines:
842
851
                    # read the entire thing
843
 
                    content, _ = knit_data._parse_record(version_id, raw_data)
 
852
                    content, _ = knit._parse_record(key[-1], raw_data)
844
853
                    if len(references[-1]) == 0:
845
854
                        line_iterator = factory.get_fulltext_content(content)
846
855
                    else:
847
856
                        line_iterator = factory.get_linedelta_content(content)
848
857
                    for line in line_iterator:
849
 
                        yield line, version_id
 
858
                        yield line, key
850
859
                else:
851
860
                    # check the header only
852
 
                    df, _ = knit_data._parse_record_header(version_id, raw_data)
 
861
                    df, _ = knit._parse_record_header(key, raw_data)
853
862
                    df.close()
854
863
                pos, size = writer.add_bytes_record(raw_data, names)
855
864
                write_index.add_node(key, eol_flag + "%d %d" % (pos, size), references)
911
920
        """Use up the inv_lines generator and setup a text key filter."""
912
921
        repo = self._pack_collection.repo
913
922
        fileid_revisions = repo._find_file_ids_from_xml_inventory_lines(
914
 
            inv_lines, self.revision_ids)
 
923
            inv_lines, self.revision_keys)
915
924
        text_filter = []
916
925
        for fileid, file_revids in fileid_revisions.iteritems():
917
926
            text_filter.extend([(fileid, file_revid) for file_revid in file_revids])
968
977
        # TODO: combine requests in the same index that are in ascending order.
969
978
        return total, requests
970
979
 
 
980
    def open_pack(self):
 
981
        """Open a pack for the pack we are creating."""
 
982
        new_pack = super(OptimisingPacker, self).open_pack()
 
983
        # Turn on the optimization flags for all the index builders.
 
984
        new_pack.revision_index.set_optimize(for_size=True)
 
985
        new_pack.inventory_index.set_optimize(for_size=True)
 
986
        new_pack.text_index.set_optimize(for_size=True)
 
987
        new_pack.signature_index.set_optimize(for_size=True)
 
988
        return new_pack
 
989
 
971
990
 
972
991
class ReconcilePacker(Packer):
973
992
    """A packer which regenerates indices etc as it copies.
1064
1083
            self.new_pack.text_index,
1065
1084
            ('blank', ), 1,
1066
1085
            add_nodes_callback=self.new_pack.text_index.add_nodes)
1067
 
        knit_index = KnitGraphIndex(file_id_index,
1068
 
            add_callback=file_id_index.add_nodes,
1069
 
            deltas=True, parents=True)
1070
 
        output_knit = knit.KnitVersionedFile('reconcile-texts',
1071
 
            self._pack_collection.transport,
1072
 
            index=knit_index,
1073
 
            access_method=_PackAccess(
1074
 
                {self.new_pack.text_index:self.new_pack.access_tuple()},
1075
 
                (self.new_pack._writer, self.new_pack.text_index)),
1076
 
            factory=knit.KnitPlainFactory())
 
1086
        data_access = _DirectPackAccess(
 
1087
                {self.new_pack.text_index:self.new_pack.access_tuple()})
 
1088
        data_access.set_writer(self.new_pack._writer, self.new_pack.text_index,
 
1089
            self.new_pack.access_tuple())
 
1090
        output_texts = KnitVersionedFiles(
 
1091
            _KnitGraphIndex(self.new_pack.text_index,
 
1092
                add_callback=self.new_pack.text_index.add_nodes,
 
1093
                deltas=True, parents=True, is_locked=repo.is_locked),
 
1094
            data_access=data_access, max_delta_chain=200)
1077
1095
        for key, parent_keys in bad_texts:
1078
1096
            # We refer to the new pack to delta data being output.
1079
1097
            # A possible improvement would be to catch errors on short reads
1086
1104
                    raise errors.BzrError('Mismatched key parent %r:%r' %
1087
1105
                        (key, parent_keys))
1088
1106
                parents.append(parent_key[1])
1089
 
            source_weave = repo.weave_store.get_weave(key[0], transaction)
1090
 
            text_lines = source_weave.get_lines(key[1])
1091
 
            # adapt the 'knit' to the current file_id.
1092
 
            file_id_index = GraphIndexPrefixAdapter(
1093
 
                self.new_pack.text_index,
1094
 
                (key[0], ), 1,
1095
 
                add_nodes_callback=self.new_pack.text_index.add_nodes)
1096
 
            knit_index._graph_index = file_id_index
1097
 
            knit_index._add_callback = file_id_index.add_nodes
1098
 
            output_knit.add_lines_with_ghosts(
1099
 
                key[1], parents, text_lines, random_id=True, check_content=False)
 
1107
            text_lines = osutils.split_lines(repo.texts.get_record_stream(
 
1108
                [key], 'unordered', True).next().get_bytes_as('fulltext'))
 
1109
            output_texts.add_lines(key, parent_keys, text_lines,
 
1110
                random_id=True, check_content=False)
1100
1111
        # 5) check that nothing inserted has a reference outside the keyspace.
1101
1112
        missing_text_keys = self.new_pack._external_compression_parents_of_texts()
1102
1113
        if missing_text_keys:
1120
1131
 
1121
1132
 
1122
1133
class RepositoryPackCollection(object):
1123
 
    """Management of packs within a repository."""
 
1134
    """Management of packs within a repository.
 
1135
    
 
1136
    :ivar _names: map of {pack_name: (index_size,)}
 
1137
    """
1124
1138
 
1125
1139
    def __init__(self, repo, transport, index_transport, upload_transport,
1126
 
                 pack_transport):
 
1140
                 pack_transport, index_builder_class, index_class):
1127
1141
        """Create a new RepositoryPackCollection.
1128
1142
 
1129
1143
        :param transport: Addresses the repository base directory 
1132
1146
        :param upload_transport: Addresses the directory into which packs are written
1133
1147
            while they're being created.
1134
1148
        :param pack_transport: Addresses the directory of existing complete packs.
 
1149
        :param index_builder_class: The index builder class to use.
 
1150
        :param index_class: The index class to use.
1135
1151
        """
1136
1152
        self.repo = repo
1137
1153
        self.transport = transport
1138
1154
        self._index_transport = index_transport
1139
1155
        self._upload_transport = upload_transport
1140
1156
        self._pack_transport = pack_transport
 
1157
        self._index_builder_class = index_builder_class
 
1158
        self._index_class = index_class
1141
1159
        self._suffix_offsets = {'.rix': 0, '.iix': 1, '.tix': 2, '.six': 3}
1142
1160
        self.packs = []
1143
1161
        # name:Pack mapping
1147
1165
        # when a pack is being created by this object, the state of that pack.
1148
1166
        self._new_pack = None
1149
1167
        # aggregated revision index data
1150
 
        self.revision_index = AggregateIndex()
1151
 
        self.inventory_index = AggregateIndex()
1152
 
        self.text_index = AggregateIndex()
1153
 
        self.signature_index = AggregateIndex()
 
1168
        self.revision_index = AggregateIndex(self.reload_pack_names)
 
1169
        self.inventory_index = AggregateIndex(self.reload_pack_names)
 
1170
        self.text_index = AggregateIndex(self.reload_pack_names)
 
1171
        self.signature_index = AggregateIndex(self.reload_pack_names)
1154
1172
 
1155
1173
    def add_pack_to_memory(self, pack):
1156
1174
        """Make a Pack object available to the repository to satisfy queries.
1166
1184
        self.text_index.add_index(pack.text_index, pack)
1167
1185
        self.signature_index.add_index(pack.signature_index, pack)
1168
1186
        
1169
 
    def _add_text_to_weave(self, file_id, revision_id, new_lines, parents,
1170
 
        nostore_sha, random_revid):
1171
 
        file_id_index = GraphIndexPrefixAdapter(
1172
 
            self.text_index.combined_index,
1173
 
            (file_id, ), 1,
1174
 
            add_nodes_callback=self.text_index.add_callback)
1175
 
        self.repo._text_knit._index._graph_index = file_id_index
1176
 
        self.repo._text_knit._index._add_callback = file_id_index.add_nodes
1177
 
        return self.repo._text_knit.add_lines_with_ghosts(
1178
 
            revision_id, parents, new_lines, nostore_sha=nostore_sha,
1179
 
            random_id=random_revid, check_content=False)[0:2]
1180
 
 
1181
1187
    def all_packs(self):
1182
1188
        """Return a list of all the Pack objects this repository has.
1183
1189
 
1306
1312
        # plan out what packs to keep, and what to reorganise
1307
1313
        while len(existing_packs):
1308
1314
            # take the largest pack, and if its less than the head of the
1309
 
            # distribution chart we will include its contents in the new pack for
1310
 
            # that position. If its larger, we remove its size from the
 
1315
            # distribution chart we will include its contents in the new pack
 
1316
            # for that position. If its larger, we remove its size from the
1311
1317
            # distribution chart
1312
1318
            next_pack_rev_count, next_pack = existing_packs.pop(0)
1313
1319
            if next_pack_rev_count >= pack_distribution[0]:
1330
1336
                    # this pack is used up, shift left.
1331
1337
                    del pack_distribution[0]
1332
1338
                    pack_operations.append([0, []])
1333
 
        
1334
 
        return pack_operations
 
1339
        # Now that we know which pack files we want to move, shove them all
 
1340
        # into a single pack file.
 
1341
        final_rev_count = 0
 
1342
        final_pack_list = []
 
1343
        for num_revs, pack_files in pack_operations:
 
1344
            final_rev_count += num_revs
 
1345
            final_pack_list.extend(pack_files)
 
1346
        if len(final_pack_list) == 1:
 
1347
            raise AssertionError('We somehow generated an autopack with a'
 
1348
                ' single pack file being moved.')
 
1349
            return []
 
1350
        return [[final_rev_count, final_pack_list]]
1335
1351
 
1336
1352
    def ensure_loaded(self):
1337
1353
        # NB: if you see an assertion error here, its probably access against
1390
1406
        detect updates from others during our write operation.
1391
1407
        :return: An iterator of the index contents.
1392
1408
        """
1393
 
        return GraphIndex(self.transport, 'pack-names', None
 
1409
        return self._index_class(self.transport, 'pack-names', None
1394
1410
                ).iter_all_entries()
1395
1411
 
1396
1412
    def _make_index(self, name, suffix):
1397
1413
        size_offset = self._suffix_offsets[suffix]
1398
1414
        index_name = name + suffix
1399
1415
        index_size = self._names[name][size_offset]
1400
 
        return GraphIndex(
 
1416
        return self._index_class(
1401
1417
            self._index_transport, index_name, index_size)
1402
1418
 
1403
1419
    def _max_pack_count(self, total_revisions):
1469
1485
        self._names.pop(pack.name)
1470
1486
        self._packs_by_name.pop(pack.name)
1471
1487
        self._remove_pack_indices(pack)
 
1488
        self.packs.remove(pack)
1472
1489
 
1473
1490
    def _remove_pack_indices(self, pack):
1474
1491
        """Remove the indices for pack from the aggregated indices."""
1553
1570
        """Release the mutex around the pack-names index."""
1554
1571
        self.repo.control_files.unlock()
1555
1572
 
1556
 
    def _save_pack_names(self, clear_obsolete_packs=False):
1557
 
        """Save the list of packs.
1558
 
 
1559
 
        This will take out the mutex around the pack names list for the
1560
 
        duration of the method call. If concurrent updates have been made, a
1561
 
        three-way merge between the current list and the current in memory list
1562
 
        is performed.
1563
 
 
1564
 
        :param clear_obsolete_packs: If True, clear out the contents of the
1565
 
            obsolete_packs directory.
1566
 
        """
1567
 
        self.lock_names()
1568
 
        try:
1569
 
            builder = GraphIndexBuilder()
1570
 
            # load the disk nodes across
1571
 
            disk_nodes = set()
1572
 
            for index, key, value in self._iter_disk_pack_index():
1573
 
                disk_nodes.add((key, value))
1574
 
            # do a two-way diff against our original content
1575
 
            current_nodes = set()
1576
 
            for name, sizes in self._names.iteritems():
1577
 
                current_nodes.add(
1578
 
                    ((name, ), ' '.join(str(size) for size in sizes)))
1579
 
            deleted_nodes = self._packs_at_load - current_nodes
1580
 
            new_nodes = current_nodes - self._packs_at_load
1581
 
            disk_nodes.difference_update(deleted_nodes)
1582
 
            disk_nodes.update(new_nodes)
1583
 
            # TODO: handle same-name, index-size-changes here - 
1584
 
            # e.g. use the value from disk, not ours, *unless* we're the one
1585
 
            # changing it.
1586
 
            for key, value in disk_nodes:
1587
 
                builder.add_node(key, value)
1588
 
            self.transport.put_file('pack-names', builder.finish(),
1589
 
                mode=self.repo.bzrdir._get_file_mode())
1590
 
            # move the baseline forward
1591
 
            self._packs_at_load = disk_nodes
1592
 
            if clear_obsolete_packs:
1593
 
                self._clear_obsolete_packs()
1594
 
        finally:
1595
 
            self._unlock_names()
1596
 
        # synchronise the memory packs list with what we just wrote:
 
1573
    def _diff_pack_names(self):
 
1574
        """Read the pack names from disk, and compare it to the one in memory.
 
1575
 
 
1576
        :return: (disk_nodes, deleted_nodes, new_nodes)
 
1577
            disk_nodes    The final set of nodes that should be referenced
 
1578
            deleted_nodes Nodes which have been removed from when we started
 
1579
            new_nodes     Nodes that are newly introduced
 
1580
        """
 
1581
        # load the disk nodes across
 
1582
        disk_nodes = set()
 
1583
        for index, key, value in self._iter_disk_pack_index():
 
1584
            disk_nodes.add((key, value))
 
1585
 
 
1586
        # do a two-way diff against our original content
 
1587
        current_nodes = set()
 
1588
        for name, sizes in self._names.iteritems():
 
1589
            current_nodes.add(
 
1590
                ((name, ), ' '.join(str(size) for size in sizes)))
 
1591
 
 
1592
        # Packs no longer present in the repository, which were present when we
 
1593
        # locked the repository
 
1594
        deleted_nodes = self._packs_at_load - current_nodes
 
1595
        # Packs which this process is adding
 
1596
        new_nodes = current_nodes - self._packs_at_load
 
1597
 
 
1598
        # Update the disk_nodes set to include the ones we are adding, and
 
1599
        # remove the ones which were removed by someone else
 
1600
        disk_nodes.difference_update(deleted_nodes)
 
1601
        disk_nodes.update(new_nodes)
 
1602
 
 
1603
        return disk_nodes, deleted_nodes, new_nodes
 
1604
 
 
1605
    def _syncronize_pack_names_from_disk_nodes(self, disk_nodes):
 
1606
        """Given the correct set of pack files, update our saved info.
 
1607
 
 
1608
        :return: (removed, added, modified)
 
1609
            removed     pack names removed from self._names
 
1610
            added       pack names added to self._names
 
1611
            modified    pack names that had changed value
 
1612
        """
 
1613
        removed = []
 
1614
        added = []
 
1615
        modified = []
 
1616
        ## self._packs_at_load = disk_nodes
1597
1617
        new_names = dict(disk_nodes)
1598
1618
        # drop no longer present nodes
1599
1619
        for pack in self.all_packs():
1600
1620
            if (pack.name,) not in new_names:
 
1621
                removed.append(pack.name)
1601
1622
                self._remove_pack_from_memory(pack)
1602
1623
        # add new nodes/refresh existing ones
1603
1624
        for key, value in disk_nodes:
1617
1638
                    self._remove_pack_from_memory(self.get_pack_by_name(name))
1618
1639
                    self._names[name] = sizes
1619
1640
                    self.get_pack_by_name(name)
 
1641
                    modified.append(name)
1620
1642
            else:
1621
1643
                # new
1622
1644
                self._names[name] = sizes
1623
1645
                self.get_pack_by_name(name)
 
1646
                added.append(name)
 
1647
        return removed, added, modified
 
1648
 
 
1649
    def _save_pack_names(self, clear_obsolete_packs=False):
 
1650
        """Save the list of packs.
 
1651
 
 
1652
        This will take out the mutex around the pack names list for the
 
1653
        duration of the method call. If concurrent updates have been made, a
 
1654
        three-way merge between the current list and the current in memory list
 
1655
        is performed.
 
1656
 
 
1657
        :param clear_obsolete_packs: If True, clear out the contents of the
 
1658
            obsolete_packs directory.
 
1659
        """
 
1660
        self.lock_names()
 
1661
        try:
 
1662
            builder = self._index_builder_class()
 
1663
            disk_nodes, deleted_nodes, new_nodes = self._diff_pack_names()
 
1664
            # TODO: handle same-name, index-size-changes here - 
 
1665
            # e.g. use the value from disk, not ours, *unless* we're the one
 
1666
            # changing it.
 
1667
            for key, value in disk_nodes:
 
1668
                builder.add_node(key, value)
 
1669
            self.transport.put_file('pack-names', builder.finish(),
 
1670
                mode=self.repo.bzrdir._get_file_mode())
 
1671
            # move the baseline forward
 
1672
            self._packs_at_load = disk_nodes
 
1673
            if clear_obsolete_packs:
 
1674
                self._clear_obsolete_packs()
 
1675
        finally:
 
1676
            self._unlock_names()
 
1677
        # synchronise the memory packs list with what we just wrote:
 
1678
        self._syncronize_pack_names_from_disk_nodes(disk_nodes)
 
1679
 
 
1680
    def reload_pack_names(self):
 
1681
        """Sync our pack listing with what is present in the repository.
 
1682
 
 
1683
        This should be called when we find out that something we thought was
 
1684
        present is now missing. This happens when another process re-packs the
 
1685
        repository, etc.
 
1686
        """
 
1687
        # This is functionally similar to _save_pack_names, but we don't write
 
1688
        # out the new value.
 
1689
        disk_nodes, _, _ = self._diff_pack_names()
 
1690
        self._packs_at_load = disk_nodes
 
1691
        (removed, added,
 
1692
         modified) = self._syncronize_pack_names_from_disk_nodes(disk_nodes)
 
1693
        if removed or added or modified:
 
1694
            return True
 
1695
        return False
1624
1696
 
1625
1697
    def _clear_obsolete_packs(self):
1626
1698
        """Delete everything from the obsolete-packs directory.
1638
1710
            raise errors.NotWriteLocked(self)
1639
1711
        self._new_pack = NewPack(self._upload_transport, self._index_transport,
1640
1712
            self._pack_transport, upload_suffix='.pack',
1641
 
            file_mode=self.repo.bzrdir._get_file_mode())
 
1713
            file_mode=self.repo.bzrdir._get_file_mode(),
 
1714
            index_builder_class=self._index_builder_class,
 
1715
            index_class=self._index_class)
1642
1716
        # allow writing: queue writes to a new index
1643
1717
        self.revision_index.add_writable_index(self._new_pack.revision_index,
1644
1718
            self._new_pack)
1649
1723
        self.signature_index.add_writable_index(self._new_pack.signature_index,
1650
1724
            self._new_pack)
1651
1725
 
1652
 
        # reused revision and signature knits may need updating
1653
 
        #
1654
 
        # "Hysterical raisins. client code in bzrlib grabs those knits outside
1655
 
        # of write groups and then mutates it inside the write group."
1656
 
        if self.repo._revision_knit is not None:
1657
 
            self.repo._revision_knit._index._add_callback = \
1658
 
                self.revision_index.add_callback
1659
 
        if self.repo._signature_knit is not None:
1660
 
            self.repo._signature_knit._index._add_callback = \
1661
 
                self.signature_index.add_callback
1662
 
        # create a reused knit object for text addition in commit.
1663
 
        self.repo._text_knit = self.repo.weave_store.get_weave_or_empty(
1664
 
            'all-texts', None)
 
1726
        self.repo.inventories._index._add_callback = self.inventory_index.add_callback
 
1727
        self.repo.revisions._index._add_callback = self.revision_index.add_callback
 
1728
        self.repo.signatures._index._add_callback = self.signature_index.add_callback
 
1729
        self.repo.texts._index._add_callback = self.text_index.add_callback
1665
1730
 
1666
1731
    def _abort_write_group(self):
1667
1732
        # FIXME: just drop the transient index.
1689
1754
        self.repo._text_knit = None
1690
1755
 
1691
1756
 
1692
 
class KnitPackRevisionStore(KnitRevisionStore):
1693
 
    """An object to adapt access from RevisionStore's to use KnitPacks.
1694
 
 
1695
 
    This class works by replacing the original RevisionStore.
1696
 
    We need to do this because the KnitPackRevisionStore is less
1697
 
    isolated in its layering - it uses services from the repo.
1698
 
    """
1699
 
 
1700
 
    def __init__(self, repo, transport, revisionstore):
1701
 
        """Create a KnitPackRevisionStore on repo with revisionstore.
1702
 
 
1703
 
        This will store its state in the Repository, use the
1704
 
        indices to provide a KnitGraphIndex,
1705
 
        and at the end of transactions write new indices.
1706
 
        """
1707
 
        KnitRevisionStore.__init__(self, revisionstore.versioned_file_store)
1708
 
        self.repo = repo
1709
 
        self._serializer = revisionstore._serializer
1710
 
        self.transport = transport
1711
 
 
1712
 
    def get_revision_file(self, transaction):
1713
 
        """Get the revision versioned file object."""
1714
 
        if getattr(self.repo, '_revision_knit', None) is not None:
1715
 
            return self.repo._revision_knit
1716
 
        self.repo._pack_collection.ensure_loaded()
1717
 
        add_callback = self.repo._pack_collection.revision_index.add_callback
1718
 
        # setup knit specific objects
1719
 
        knit_index = KnitGraphIndex(
1720
 
            self.repo._pack_collection.revision_index.combined_index,
1721
 
            add_callback=add_callback)
1722
 
        self.repo._revision_knit = knit.KnitVersionedFile(
1723
 
            'revisions', self.transport.clone('..'),
1724
 
            self.repo.bzrdir._get_file_mode(),
1725
 
            create=False,
1726
 
            index=knit_index, delta=False, factory=knit.KnitPlainFactory(),
1727
 
            access_method=self.repo._pack_collection.revision_index.knit_access)
1728
 
        return self.repo._revision_knit
1729
 
 
1730
 
    def get_signature_file(self, transaction):
1731
 
        """Get the signature versioned file object."""
1732
 
        if getattr(self.repo, '_signature_knit', None) is not None:
1733
 
            return self.repo._signature_knit
1734
 
        self.repo._pack_collection.ensure_loaded()
1735
 
        add_callback = self.repo._pack_collection.signature_index.add_callback
1736
 
        # setup knit specific objects
1737
 
        knit_index = KnitGraphIndex(
1738
 
            self.repo._pack_collection.signature_index.combined_index,
1739
 
            add_callback=add_callback, parents=False)
1740
 
        self.repo._signature_knit = knit.KnitVersionedFile(
1741
 
            'signatures', self.transport.clone('..'),
1742
 
            self.repo.bzrdir._get_file_mode(),
1743
 
            create=False,
1744
 
            index=knit_index, delta=False, factory=knit.KnitPlainFactory(),
1745
 
            access_method=self.repo._pack_collection.signature_index.knit_access)
1746
 
        return self.repo._signature_knit
1747
 
 
1748
 
 
1749
 
class KnitPackTextStore(VersionedFileStore):
1750
 
    """Presents a TextStore abstraction on top of packs.
1751
 
 
1752
 
    This class works by replacing the original VersionedFileStore.
1753
 
    We need to do this because the KnitPackRevisionStore is less
1754
 
    isolated in its layering - it uses services from the repo and shares them
1755
 
    with all the data written in a single write group.
1756
 
    """
1757
 
 
1758
 
    def __init__(self, repo, transport, weavestore):
1759
 
        """Create a KnitPackTextStore on repo with weavestore.
1760
 
 
1761
 
        This will store its state in the Repository, use the
1762
 
        indices FileNames to provide a KnitGraphIndex,
1763
 
        and at the end of transactions write new indices.
1764
 
        """
1765
 
        # don't call base class constructor - it's not suitable.
1766
 
        # no transient data stored in the transaction
1767
 
        # cache.
1768
 
        self._precious = False
1769
 
        self.repo = repo
1770
 
        self.transport = transport
1771
 
        self.weavestore = weavestore
1772
 
        # XXX for check() which isn't updated yet
1773
 
        self._transport = weavestore._transport
1774
 
 
1775
 
    def get_weave_or_empty(self, file_id, transaction):
1776
 
        """Get a 'Knit' backed by the .tix indices.
1777
 
 
1778
 
        The transaction parameter is ignored.
1779
 
        """
1780
 
        self.repo._pack_collection.ensure_loaded()
1781
 
        add_callback = self.repo._pack_collection.text_index.add_callback
1782
 
        # setup knit specific objects
1783
 
        file_id_index = GraphIndexPrefixAdapter(
1784
 
            self.repo._pack_collection.text_index.combined_index,
1785
 
            (file_id, ), 1, add_nodes_callback=add_callback)
1786
 
        knit_index = KnitGraphIndex(file_id_index,
1787
 
            add_callback=file_id_index.add_nodes,
1788
 
            deltas=True, parents=True)
1789
 
        return knit.KnitVersionedFile('text:' + file_id,
1790
 
            self.transport.clone('..'),
1791
 
            None,
1792
 
            index=knit_index,
1793
 
            access_method=self.repo._pack_collection.text_index.knit_access,
1794
 
            factory=knit.KnitPlainFactory())
1795
 
 
1796
 
    get_weave = get_weave_or_empty
1797
 
 
1798
 
    def __iter__(self):
1799
 
        """Generate a list of the fileids inserted, for use by check."""
1800
 
        self.repo._pack_collection.ensure_loaded()
1801
 
        ids = set()
1802
 
        for index, key, value, refs in \
1803
 
            self.repo._pack_collection.text_index.combined_index.iter_all_entries():
1804
 
            ids.add(key[0])
1805
 
        return iter(ids)
1806
 
 
1807
 
 
1808
 
class InventoryKnitThunk(object):
1809
 
    """An object to manage thunking get_inventory_weave to pack based knits."""
1810
 
 
1811
 
    def __init__(self, repo, transport):
1812
 
        """Create an InventoryKnitThunk for repo at transport.
1813
 
 
1814
 
        This will store its state in the Repository, use the
1815
 
        indices FileNames to provide a KnitGraphIndex,
1816
 
        and at the end of transactions write a new index..
1817
 
        """
1818
 
        self.repo = repo
1819
 
        self.transport = transport
1820
 
 
1821
 
    def get_weave(self):
1822
 
        """Get a 'Knit' that contains inventory data."""
1823
 
        self.repo._pack_collection.ensure_loaded()
1824
 
        add_callback = self.repo._pack_collection.inventory_index.add_callback
1825
 
        # setup knit specific objects
1826
 
        knit_index = KnitGraphIndex(
1827
 
            self.repo._pack_collection.inventory_index.combined_index,
1828
 
            add_callback=add_callback, deltas=True, parents=True)
1829
 
        return knit.KnitVersionedFile(
1830
 
            'inventory', self.transport.clone('..'),
1831
 
            self.repo.bzrdir._get_file_mode(),
1832
 
            create=False,
1833
 
            index=knit_index, delta=True, factory=knit.KnitPlainFactory(),
1834
 
            access_method=self.repo._pack_collection.inventory_index.knit_access)
1835
 
 
1836
 
 
1837
1757
class KnitPackRepository(KnitRepository):
1838
 
    """Repository with knit objects stored inside pack containers."""
1839
 
 
1840
 
    def __init__(self, _format, a_bzrdir, control_files, _revision_store,
1841
 
        control_store, text_store, _commit_builder_class, _serializer):
 
1758
    """Repository with knit objects stored inside pack containers.
 
1759
    
 
1760
    The layering for a KnitPackRepository is:
 
1761
 
 
1762
    Graph        |  HPSS    | Repository public layer |
 
1763
    ===================================================
 
1764
    Tuple based apis below, string based, and key based apis above
 
1765
    ---------------------------------------------------
 
1766
    KnitVersionedFiles
 
1767
      Provides .texts, .revisions etc
 
1768
      This adapts the N-tuple keys to physical knit records which only have a
 
1769
      single string identifier (for historical reasons), which in older formats
 
1770
      was always the revision_id, and in the mapped code for packs is always
 
1771
      the last element of key tuples.
 
1772
    ---------------------------------------------------
 
1773
    GraphIndex
 
1774
      A separate GraphIndex is used for each of the
 
1775
      texts/inventories/revisions/signatures contained within each individual
 
1776
      pack file. The GraphIndex layer works in N-tuples and is unaware of any
 
1777
      semantic value.
 
1778
    ===================================================
 
1779
    
 
1780
    """
 
1781
 
 
1782
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
 
1783
        _serializer):
1842
1784
        KnitRepository.__init__(self, _format, a_bzrdir, control_files,
1843
 
            _revision_store, control_store, text_store, _commit_builder_class,
1844
 
            _serializer)
 
1785
            _commit_builder_class, _serializer)
1845
1786
        index_transport = self._transport.clone('indices')
1846
 
        self._pack_collection = RepositoryPackCollection(self,
1847
 
            self._transport,
 
1787
        self._pack_collection = RepositoryPackCollection(self, self._transport,
1848
1788
            index_transport,
1849
1789
            self._transport.clone('upload'),
1850
 
            self._transport.clone('packs'))
1851
 
        self._revision_store = KnitPackRevisionStore(self, index_transport, self._revision_store)
1852
 
        self.weave_store = KnitPackTextStore(self, index_transport, self.weave_store)
1853
 
        self._inv_thunk = InventoryKnitThunk(self, index_transport)
 
1790
            self._transport.clone('packs'),
 
1791
            _format.index_builder_class,
 
1792
            _format.index_class)
 
1793
        self.inventories = KnitVersionedFiles(
 
1794
            _KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
 
1795
                add_callback=self._pack_collection.inventory_index.add_callback,
 
1796
                deltas=True, parents=True, is_locked=self.is_locked),
 
1797
            data_access=self._pack_collection.inventory_index.data_access,
 
1798
            max_delta_chain=200)
 
1799
        self.revisions = KnitVersionedFiles(
 
1800
            _KnitGraphIndex(self._pack_collection.revision_index.combined_index,
 
1801
                add_callback=self._pack_collection.revision_index.add_callback,
 
1802
                deltas=False, parents=True, is_locked=self.is_locked),
 
1803
            data_access=self._pack_collection.revision_index.data_access,
 
1804
            max_delta_chain=0)
 
1805
        self.signatures = KnitVersionedFiles(
 
1806
            _KnitGraphIndex(self._pack_collection.signature_index.combined_index,
 
1807
                add_callback=self._pack_collection.signature_index.add_callback,
 
1808
                deltas=False, parents=False, is_locked=self.is_locked),
 
1809
            data_access=self._pack_collection.signature_index.data_access,
 
1810
            max_delta_chain=0)
 
1811
        self.texts = KnitVersionedFiles(
 
1812
            _KnitGraphIndex(self._pack_collection.text_index.combined_index,
 
1813
                add_callback=self._pack_collection.text_index.add_callback,
 
1814
                deltas=True, parents=True, is_locked=self.is_locked),
 
1815
            data_access=self._pack_collection.text_index.data_access,
 
1816
            max_delta_chain=200)
1854
1817
        # True when the repository object is 'write locked' (as opposed to the
1855
1818
        # physical lock only taken out around changes to the pack-names list.) 
1856
1819
        # Another way to represent this would be a decorator around the control
1862
1825
        self._reconcile_does_inventory_gc = True
1863
1826
        self._reconcile_fixes_text_parents = True
1864
1827
        self._reconcile_backsup_inventory = False
 
1828
        self._fetch_order = 'unordered'
 
1829
 
 
1830
    def _warn_if_deprecated(self):
 
1831
        # This class isn't deprecated, but one sub-format is
 
1832
        if isinstance(self._format, RepositoryFormatKnitPack5RichRootBroken):
 
1833
            from bzrlib import repository
 
1834
            if repository._deprecation_warning_done:
 
1835
                return
 
1836
            repository._deprecation_warning_done = True
 
1837
            warning("Format %s for %s is deprecated - please use"
 
1838
                    " 'bzr upgrade --1.6.1-rich-root'"
 
1839
                    % (self._format, self.bzrdir.transport.base))
1865
1840
 
1866
1841
    def _abort_write_group(self):
1867
1842
        self._pack_collection._abort_write_group()
1912
1887
        parent_map = self.get_parent_map(revision_ids)
1913
1888
        return [parent_map.get(r, None) for r in revision_ids]
1914
1889
 
1915
 
    def get_parent_map(self, keys):
1916
 
        """See graph._StackedParentsProvider.get_parent_map
1917
 
 
1918
 
        This implementation accesses the combined revision index to provide
1919
 
        answers.
1920
 
        """
1921
 
        self._pack_collection.ensure_loaded()
1922
 
        index = self._pack_collection.revision_index.combined_index
1923
 
        keys = set(keys)
1924
 
        if None in keys:
1925
 
            raise ValueError('get_parent_map(None) is not valid')
1926
 
        if _mod_revision.NULL_REVISION in keys:
1927
 
            keys.discard(_mod_revision.NULL_REVISION)
1928
 
            found_parents = {_mod_revision.NULL_REVISION:()}
1929
 
        else:
1930
 
            found_parents = {}
1931
 
        search_keys = set((revision_id,) for revision_id in keys)
1932
 
        for index, key, value, refs in index.iter_entries(search_keys):
1933
 
            parents = refs[0]
1934
 
            if not parents:
1935
 
                parents = (_mod_revision.NULL_REVISION,)
1936
 
            else:
1937
 
                parents = tuple(parent[0] for parent in parents)
1938
 
            found_parents[key[0]] = parents
1939
 
        return found_parents
1940
 
 
1941
 
    def has_revisions(self, revision_ids):
1942
 
        """See Repository.has_revisions()."""
1943
 
        revision_ids = set(revision_ids)
1944
 
        result = revision_ids.intersection(
1945
 
            set([None, _mod_revision.NULL_REVISION]))
1946
 
        revision_ids.difference_update(result)
1947
 
        index = self._pack_collection.revision_index.combined_index
1948
 
        keys = [(revision_id,) for revision_id in revision_ids]
1949
 
        result.update(node[1][0] for node in index.iter_entries(keys))
1950
 
        return result
1951
 
 
1952
1890
    def _make_parents_provider(self):
1953
1891
        return graph.CachingParentsProvider(self)
1954
1892
 
1968
1906
    def _commit_write_group(self):
1969
1907
        return self._pack_collection._commit_write_group()
1970
1908
 
1971
 
    def get_inventory_weave(self):
1972
 
        return self._inv_thunk.get_weave()
1973
 
 
1974
1909
    def get_transaction(self):
1975
1910
        if self._write_lock_count:
1976
1911
            return self._transaction
1988
1923
            raise errors.ReadOnlyError(self)
1989
1924
        self._write_lock_count += 1
1990
1925
        if self._write_lock_count == 1:
1991
 
            from bzrlib import transactions
1992
1926
            self._transaction = transactions.WriteTransaction()
 
1927
            for repo in self._fallback_repositories:
 
1928
                # Writes don't affect fallback repos
 
1929
                repo.lock_read()
1993
1930
        self._refresh_data()
1994
1931
 
1995
1932
    def lock_read(self):
1997
1934
            self._write_lock_count += 1
1998
1935
        else:
1999
1936
            self.control_files.lock_read()
 
1937
            for repo in self._fallback_repositories:
 
1938
                # Writes don't affect fallback repos
 
1939
                repo.lock_read()
2000
1940
        self._refresh_data()
2001
1941
 
2002
1942
    def leave_lock_in_place(self):
2038
1978
                transaction = self._transaction
2039
1979
                self._transaction = None
2040
1980
                transaction.finish()
 
1981
                for repo in self._fallback_repositories:
 
1982
                    repo.unlock()
2041
1983
        else:
2042
1984
            self.control_files.unlock()
 
1985
            for repo in self._fallback_repositories:
 
1986
                repo.unlock()
2043
1987
 
2044
1988
 
2045
1989
class RepositoryFormatPack(MetaDirRepositoryFormat):
2069
2013
    _serializer = None
2070
2014
    # External references are not supported in pack repositories yet.
2071
2015
    supports_external_lookups = False
2072
 
 
2073
 
    def _get_control_store(self, repo_transport, control_files):
2074
 
        """Return the control store for this repository."""
2075
 
        return VersionedFileStore(
2076
 
            repo_transport,
2077
 
            prefixed=False,
2078
 
            file_mode=control_files._file_mode,
2079
 
            versionedfile_class=knit.make_file_knit,
2080
 
            versionedfile_kwargs={'factory': knit.KnitPlainFactory()},
2081
 
            )
2082
 
 
2083
 
    def _get_revision_store(self, repo_transport, control_files):
2084
 
        """See RepositoryFormat._get_revision_store()."""
2085
 
        versioned_file_store = VersionedFileStore(
2086
 
            repo_transport,
2087
 
            file_mode=control_files._file_mode,
2088
 
            prefixed=False,
2089
 
            precious=True,
2090
 
            versionedfile_class=knit.make_file_knit,
2091
 
            versionedfile_kwargs={'delta': False,
2092
 
                                  'factory': knit.KnitPlainFactory(),
2093
 
                                 },
2094
 
            escaped=True,
2095
 
            )
2096
 
        return KnitRevisionStore(versioned_file_store)
2097
 
 
2098
 
    def _get_text_store(self, transport, control_files):
2099
 
        """See RepositoryFormat._get_text_store()."""
2100
 
        return self._get_versioned_file_store('knits',
2101
 
                                  transport,
2102
 
                                  control_files,
2103
 
                                  versionedfile_class=knit.make_file_knit,
2104
 
                                  versionedfile_kwargs={
2105
 
                                      'create_parent_dir': True,
2106
 
                                      'delay_create': True,
2107
 
                                      'dir_mode': control_files._dir_mode,
2108
 
                                  },
2109
 
                                  escaped=True)
 
2016
    # What index classes to use
 
2017
    index_builder_class = None
 
2018
    index_class = None
2110
2019
 
2111
2020
    def initialize(self, a_bzrdir, shared=False):
2112
2021
        """Create a pack based repository.
2118
2027
        """
2119
2028
        mutter('creating repository in %s.', a_bzrdir.transport.base)
2120
2029
        dirs = ['indices', 'obsolete_packs', 'packs', 'upload']
2121
 
        builder = GraphIndexBuilder()
 
2030
        builder = self.index_builder_class()
2122
2031
        files = [('pack-names', builder.finish())]
2123
2032
        utf8_files = [('format', self.get_format_string())]
2124
2033
        
2139
2048
        else:
2140
2049
            repo_transport = a_bzrdir.get_repository_transport(None)
2141
2050
        control_files = lockable_files.LockableFiles(repo_transport,
2142
 
            'lock', lockdir.LockDir)
2143
 
        text_store = self._get_text_store(repo_transport, control_files)
2144
 
        control_store = self._get_control_store(repo_transport, control_files)
2145
 
        _revision_store = self._get_revision_store(repo_transport, control_files)
 
2051
                                'lock', lockdir.LockDir)
2146
2052
        return self.repository_class(_format=self,
2147
2053
                              a_bzrdir=a_bzrdir,
2148
2054
                              control_files=control_files,
2149
 
                              _revision_store=_revision_store,
2150
 
                              control_store=control_store,
2151
 
                              text_store=text_store,
2152
2055
                              _commit_builder_class=self._commit_builder_class,
2153
2056
                              _serializer=self._serializer)
2154
2057
 
2161
2064
 
2162
2065
    repository_class = KnitPackRepository
2163
2066
    _commit_builder_class = PackCommitBuilder
2164
 
    _serializer = xml5.serializer_v5
 
2067
    @property
 
2068
    def _serializer(self):
 
2069
        return xml5.serializer_v5
 
2070
    # What index classes to use
 
2071
    index_builder_class = InMemoryGraphIndex
 
2072
    index_class = GraphIndex
2165
2073
 
2166
2074
    def _get_matching_bzrdir(self):
2167
2075
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
2197
2105
    _commit_builder_class = PackRootCommitBuilder
2198
2106
    rich_root_data = True
2199
2107
    supports_tree_reference = True
2200
 
    _serializer = xml7.serializer_v7
 
2108
    @property
 
2109
    def _serializer(self):
 
2110
        return xml7.serializer_v7
 
2111
    # What index classes to use
 
2112
    index_builder_class = InMemoryGraphIndex
 
2113
    index_class = GraphIndex
2201
2114
 
2202
2115
    def _get_matching_bzrdir(self):
2203
2116
        return bzrdir.format_registry.make_bzrdir(
2238
2151
    _commit_builder_class = PackRootCommitBuilder
2239
2152
    rich_root_data = True
2240
2153
    supports_tree_reference = False
2241
 
    _serializer = xml6.serializer_v6
 
2154
    @property
 
2155
    def _serializer(self):
 
2156
        return xml6.serializer_v6
 
2157
    # What index classes to use
 
2158
    index_builder_class = InMemoryGraphIndex
 
2159
    index_class = GraphIndex
2242
2160
 
2243
2161
    def _get_matching_bzrdir(self):
2244
2162
        return bzrdir.format_registry.make_bzrdir(
2264
2182
        return "Packs containing knits with rich root support\n"
2265
2183
 
2266
2184
 
2267
 
class RepositoryFormatPackDevelopment0(RepositoryFormatPack):
 
2185
class RepositoryFormatKnitPack5(RepositoryFormatPack):
 
2186
    """Repository that supports external references to allow stacking.
 
2187
 
 
2188
    New in release 1.6.
 
2189
 
 
2190
    Supports external lookups, which results in non-truncated ghosts after
 
2191
    reconcile compared to pack-0.92 formats.
 
2192
    """
 
2193
 
 
2194
    repository_class = KnitPackRepository
 
2195
    _commit_builder_class = PackCommitBuilder
 
2196
    supports_external_lookups = True
 
2197
    # What index classes to use
 
2198
    index_builder_class = InMemoryGraphIndex
 
2199
    index_class = GraphIndex
 
2200
 
 
2201
    @property
 
2202
    def _serializer(self):
 
2203
        return xml5.serializer_v5
 
2204
 
 
2205
    def _get_matching_bzrdir(self):
 
2206
        return bzrdir.format_registry.make_bzrdir('1.6')
 
2207
 
 
2208
    def _ignore_setting_bzrdir(self, format):
 
2209
        pass
 
2210
 
 
2211
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2212
 
 
2213
    def get_format_string(self):
 
2214
        """See RepositoryFormat.get_format_string()."""
 
2215
        return "Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n"
 
2216
 
 
2217
    def get_format_description(self):
 
2218
        """See RepositoryFormat.get_format_description()."""
 
2219
        return "Packs 5 (adds stacking support, requires bzr 1.6)"
 
2220
 
 
2221
    def check_conversion_target(self, target_format):
 
2222
        pass
 
2223
 
 
2224
 
 
2225
class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack):
 
2226
    """A repository with rich roots and stacking.
 
2227
 
 
2228
    New in release 1.6.1.
 
2229
 
 
2230
    Supports stacking on other repositories, allowing data to be accessed
 
2231
    without being stored locally.
 
2232
    """
 
2233
 
 
2234
    repository_class = KnitPackRepository
 
2235
    _commit_builder_class = PackRootCommitBuilder
 
2236
    rich_root_data = True
 
2237
    supports_tree_reference = False # no subtrees
 
2238
    supports_external_lookups = True
 
2239
    # What index classes to use
 
2240
    index_builder_class = InMemoryGraphIndex
 
2241
    index_class = GraphIndex
 
2242
 
 
2243
    @property
 
2244
    def _serializer(self):
 
2245
        return xml6.serializer_v6
 
2246
 
 
2247
    def _get_matching_bzrdir(self):
 
2248
        return bzrdir.format_registry.make_bzrdir(
 
2249
            '1.6.1-rich-root')
 
2250
 
 
2251
    def _ignore_setting_bzrdir(self, format):
 
2252
        pass
 
2253
 
 
2254
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2255
 
 
2256
    def check_conversion_target(self, target_format):
 
2257
        if not target_format.rich_root_data:
 
2258
            raise errors.BadConversionTarget(
 
2259
                'Does not support rich root data.', target_format)
 
2260
 
 
2261
    def get_format_string(self):
 
2262
        """See RepositoryFormat.get_format_string()."""
 
2263
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
 
2264
 
 
2265
    def get_format_description(self):
 
2266
        return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
 
2267
 
 
2268
 
 
2269
class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack):
 
2270
    """A repository with rich roots and external references.
 
2271
 
 
2272
    New in release 1.6.
 
2273
 
 
2274
    Supports external lookups, which results in non-truncated ghosts after
 
2275
    reconcile compared to pack-0.92 formats.
 
2276
 
 
2277
    This format was deprecated because the serializer it uses accidentally
 
2278
    supported subtrees, when the format was not intended to. This meant that
 
2279
    someone could accidentally fetch from an incorrect repository.
 
2280
    """
 
2281
 
 
2282
    repository_class = KnitPackRepository
 
2283
    _commit_builder_class = PackRootCommitBuilder
 
2284
    rich_root_data = True
 
2285
    supports_tree_reference = False # no subtrees
 
2286
 
 
2287
    supports_external_lookups = True
 
2288
    # What index classes to use
 
2289
    index_builder_class = InMemoryGraphIndex
 
2290
    index_class = GraphIndex
 
2291
 
 
2292
    @property
 
2293
    def _serializer(self):
 
2294
        return xml7.serializer_v7
 
2295
 
 
2296
    def _get_matching_bzrdir(self):
 
2297
        return bzrdir.format_registry.make_bzrdir(
 
2298
            '1.6.1-rich-root')
 
2299
 
 
2300
    def _ignore_setting_bzrdir(self, format):
 
2301
        pass
 
2302
 
 
2303
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2304
 
 
2305
    def check_conversion_target(self, target_format):
 
2306
        if not target_format.rich_root_data:
 
2307
            raise errors.BadConversionTarget(
 
2308
                'Does not support rich root data.', target_format)
 
2309
 
 
2310
    def get_format_string(self):
 
2311
        """See RepositoryFormat.get_format_string()."""
 
2312
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n"
 
2313
 
 
2314
    def get_format_description(self):
 
2315
        return ("Packs 5 rich-root (adds stacking support, requires bzr 1.6)"
 
2316
                " (deprecated)")
 
2317
 
 
2318
 
 
2319
class RepositoryFormatPackDevelopment2(RepositoryFormatPack):
2268
2320
    """A no-subtrees development repository.
2269
2321
 
2270
 
    This format should be retained until the second release after bzr 1.0.
 
2322
    This format should be retained until the second release after bzr 1.7.
2271
2323
 
2272
 
    No changes to the disk behaviour from pack-0.92.
 
2324
    This is pack-1.6.1 with B+Tree indices.
2273
2325
    """
2274
2326
 
2275
2327
    repository_class = KnitPackRepository
2276
2328
    _commit_builder_class = PackCommitBuilder
2277
 
    _serializer = xml5.serializer_v5
 
2329
    supports_external_lookups = True
 
2330
    # What index classes to use
 
2331
    index_builder_class = BTreeBuilder
 
2332
    index_class = BTreeGraphIndex
 
2333
 
 
2334
    @property
 
2335
    def _serializer(self):
 
2336
        return xml5.serializer_v5
2278
2337
 
2279
2338
    def _get_matching_bzrdir(self):
2280
 
        return bzrdir.format_registry.make_bzrdir('development0')
 
2339
        return bzrdir.format_registry.make_bzrdir('development2')
2281
2340
 
2282
2341
    def _ignore_setting_bzrdir(self, format):
2283
2342
        pass
2286
2345
 
2287
2346
    def get_format_string(self):
2288
2347
        """See RepositoryFormat.get_format_string()."""
2289
 
        return "Bazaar development format 0 (needs bzr.dev from before 1.3)\n"
 
2348
        return "Bazaar development format 2 (needs bzr.dev from before 1.8)\n"
2290
2349
 
2291
2350
    def get_format_description(self):
2292
2351
        """See RepositoryFormat.get_format_description()."""
2293
2352
        return ("Development repository format, currently the same as "
2294
 
            "pack-0.92\n")
 
2353
            "1.6.1 with B+Trees.\n")
2295
2354
 
2296
2355
    def check_conversion_target(self, target_format):
2297
2356
        pass
2298
2357
 
2299
2358
 
2300
 
class RepositoryFormatPackDevelopment0Subtree(RepositoryFormatPack):
 
2359
class RepositoryFormatPackDevelopment2Subtree(RepositoryFormatPack):
2301
2360
    """A subtrees development repository.
2302
2361
 
2303
 
    This format should be retained until the second release after bzr 1.0.
 
2362
    This format should be retained until the second release after bzr 1.7.
2304
2363
 
2305
 
    No changes to the disk behaviour from pack-0.92-subtree.
 
2364
    1.6.1-subtree[as it might have been] with B+Tree indices.
2306
2365
    """
2307
2366
 
2308
2367
    repository_class = KnitPackRepository
2309
2368
    _commit_builder_class = PackRootCommitBuilder
2310
2369
    rich_root_data = True
2311
2370
    supports_tree_reference = True
2312
 
    _serializer = xml7.serializer_v7
 
2371
    supports_external_lookups = True
 
2372
    # What index classes to use
 
2373
    index_builder_class = BTreeBuilder
 
2374
    index_class = BTreeGraphIndex
 
2375
 
 
2376
    @property
 
2377
    def _serializer(self):
 
2378
        return xml7.serializer_v7
2313
2379
 
2314
2380
    def _get_matching_bzrdir(self):
2315
2381
        return bzrdir.format_registry.make_bzrdir(
2316
 
            'development0-subtree')
 
2382
            'development2-subtree')
2317
2383
 
2318
2384
    def _ignore_setting_bzrdir(self, format):
2319
2385
        pass
2330
2396
            
2331
2397
    def get_format_string(self):
2332
2398
        """See RepositoryFormat.get_format_string()."""
2333
 
        return ("Bazaar development format 0 with subtree support "
2334
 
            "(needs bzr.dev from before 1.3)\n")
 
2399
        return ("Bazaar development format 2 with subtree support "
 
2400
            "(needs bzr.dev from before 1.8)\n")
2335
2401
 
2336
2402
    def get_format_description(self):
2337
2403
        """See RepositoryFormat.get_format_description()."""
2338
2404
        return ("Development repository format, currently the same as "
2339
 
            "pack-0.92-subtree\n")
2340
 
 
2341
 
 
 
2405
            "1.6.1-subtree with B+Tree indices.\n")