/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/pack_repo.py

1st cut merge of bzr.dev r3907

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,
 
38
    InMemoryGraphIndex,
36
39
    )
37
40
from bzrlib.knit import (
38
41
    KnitPlainFactory,
40
43
    _KnitGraphIndex,
41
44
    _DirectPackAccess,
42
45
    )
43
 
from bzrlib.osutils import rand_chars, split_lines
44
 
from bzrlib.pack import ContainerWriter
45
 
from bzrlib.store import revision
46
46
from bzrlib import tsort
47
47
""")
48
48
from bzrlib import (
50
50
    errors,
51
51
    lockable_files,
52
52
    lockdir,
53
 
    osutils,
54
53
    symbol_versioning,
55
 
    transactions,
56
 
    xml5,
57
 
    xml6,
58
 
    xml7,
59
54
    )
60
55
 
61
 
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
    )
62
65
from bzrlib.repofmt.knitrepo import KnitRepository
63
66
from bzrlib.repository import (
64
67
    CommitBuilder,
65
 
    InterRepository,
66
 
    MetaDirRepository,
67
68
    MetaDirRepositoryFormat,
68
69
    RepositoryFormat,
69
70
    RootCommitBuilder,
70
71
    )
71
72
import bzrlib.revision as _mod_revision
72
 
from bzrlib.store.versioned import VersionedFileStore
73
73
from bzrlib.trace import (
74
74
    mutter,
75
 
    mutter_callsite,
76
 
    note,
77
75
    warning,
78
76
    )
79
77
 
174
172
        """The text index is the name + .tix."""
175
173
        return self.index_name('text', name)
176
174
 
177
 
    def _external_compression_parents_of_texts(self):
178
 
        keys = set()
179
 
        refs = set()
180
 
        for node in self.text_index.iter_all_entries():
181
 
            keys.add(node[1])
182
 
            refs.update(node[3][1])
183
 
        return refs - keys
184
 
 
185
175
 
186
176
class ExistingPack(Pack):
187
177
    """An in memory proxy for an existing .pack and its disk indices."""
224
214
        'signature': ('.six', 3),
225
215
        }
226
216
 
227
 
    def __init__(self, upload_transport, index_transport, pack_transport,
228
 
        upload_suffix='', file_mode=None):
 
217
    def __init__(self, pack_collection, upload_suffix='', file_mode=None):
229
218
        """Create a NewPack instance.
230
219
 
231
 
        :param upload_transport: A writable transport for the pack to be
232
 
            incrementally uploaded to.
233
 
        :param index_transport: A writable transport for the pack's indices to
234
 
            be written to when the pack is finished.
235
 
        :param pack_transport: A writable transport for the pack to be renamed
236
 
            to when the upload is complete. This *must* be the same as
237
 
            upload_transport.clone('../packs').
 
220
        :param pack_collection: A PackCollection into which this is being inserted.
238
221
        :param upload_suffix: An optional suffix to be given to any temporary
239
222
            files created during the pack creation. e.g '.autopack'
240
 
        :param file_mode: An optional file mode to create the new files with.
 
223
        :param file_mode: Unix permissions for newly created file.
241
224
        """
242
225
        # The relative locations of the packs are constrained, but all are
243
226
        # passed in because the caller has them, so as to avoid object churn.
 
227
        index_builder_class = pack_collection._index_builder_class
244
228
        Pack.__init__(self,
245
229
            # Revisions: parents list, no text compression.
246
 
            InMemoryGraphIndex(reference_lists=1),
 
230
            index_builder_class(reference_lists=1),
247
231
            # Inventory: We want to map compression only, but currently the
248
232
            # knit code hasn't been updated enough to understand that, so we
249
233
            # have a regular 2-list index giving parents and compression
250
234
            # source.
251
 
            InMemoryGraphIndex(reference_lists=2),
 
235
            index_builder_class(reference_lists=2),
252
236
            # Texts: compression and per file graph, for all fileids - so two
253
237
            # reference lists and two elements in the key tuple.
254
 
            InMemoryGraphIndex(reference_lists=2, key_elements=2),
 
238
            index_builder_class(reference_lists=2, key_elements=2),
255
239
            # Signatures: Just blobs to store, no compression, no parents
256
240
            # listing.
257
 
            InMemoryGraphIndex(reference_lists=0),
 
241
            index_builder_class(reference_lists=0),
258
242
            )
 
243
        self._pack_collection = pack_collection
 
244
        # When we make readonly indices, we need this.
 
245
        self.index_class = pack_collection._index_class
259
246
        # where should the new pack be opened
260
 
        self.upload_transport = upload_transport
 
247
        self.upload_transport = pack_collection._upload_transport
261
248
        # where are indices written out to
262
 
        self.index_transport = index_transport
 
249
        self.index_transport = pack_collection._index_transport
263
250
        # where is the pack renamed to when it is finished?
264
 
        self.pack_transport = pack_transport
 
251
        self.pack_transport = pack_collection._pack_transport
265
252
        # What file mode to upload the pack and indices with.
266
253
        self._file_mode = file_mode
267
254
        # tracks the content written to the .pack file.
268
 
        self._hash = md5.new()
 
255
        self._hash = osutils.md5()
269
256
        # a four-tuple with the length in bytes of the indices, once the pack
270
257
        # is finalised. (rev, inv, text, sigs)
271
258
        self.index_sizes = None
275
262
        # under creation.
276
263
        self._cache_limit = 0
277
264
        # the temporary pack file name.
278
 
        self.random_name = rand_chars(20) + upload_suffix
 
265
        self.random_name = osutils.rand_chars(20) + upload_suffix
279
266
        # when was this pack started ?
280
267
        self.start_time = time.time()
281
268
        # open an output stream for the data added to the pack.
329
316
        else:
330
317
            raise AssertionError(self._state)
331
318
 
 
319
    def _check_references(self):
 
320
        """Make sure our external references are present.
 
321
        
 
322
        Packs are allowed to have deltas whose base is not in the pack, but it
 
323
        must be present somewhere in this collection.  It is not allowed to
 
324
        have deltas based on a fallback repository. 
 
325
        (See <https://bugs.launchpad.net/bzr/+bug/288751>)
 
326
        """
 
327
        missing_items = {}
 
328
        for (index_name, external_refs, index) in [
 
329
            ('texts',
 
330
                self.text_index._external_references(),
 
331
                self._pack_collection.text_index.combined_index),
 
332
            ('inventories',
 
333
                self.inventory_index._external_references(),
 
334
                self._pack_collection.inventory_index.combined_index),
 
335
            ]:
 
336
            missing = external_refs.difference(
 
337
                k for (idx, k, v, r) in 
 
338
                index.iter_entries(external_refs))
 
339
            if missing:
 
340
                missing_items[index_name] = sorted(list(missing))
 
341
        if missing_items:
 
342
            from pprint import pformat
 
343
            raise errors.BzrCheckError(
 
344
                "Newly created pack file %r has delta references to "
 
345
                "items not in its repository:\n%s"
 
346
                % (self, pformat(missing_items)))
 
347
 
332
348
    def data_inserted(self):
333
349
        """True if data has been added to this pack."""
334
350
        return bool(self.get_revision_count() or
351
367
        if self._buffer[1]:
352
368
            self._write_data('', flush=True)
353
369
        self.name = self._hash.hexdigest()
 
370
        self._check_references()
354
371
        # write indices
355
372
        # XXX: It'd be better to write them all to temporary names, then
356
373
        # rename them all into place, so that the window when only some are
402
419
 
403
420
    def _replace_index_with_readonly(self, index_type):
404
421
        setattr(self, index_type + '_index',
405
 
            GraphIndex(self.index_transport,
 
422
            self.index_class(self.index_transport,
406
423
                self.index_name(index_type, self.name),
407
424
                self.index_sizes[self.index_offset(index_type)]))
408
425
 
447
464
    # XXX: Probably 'can be written to' could/should be separated from 'acts
448
465
    # like a knit index' -- mbp 20071024
449
466
 
450
 
    def __init__(self):
451
 
        """Create an AggregateIndex."""
 
467
    def __init__(self, reload_func=None):
 
468
        """Create an AggregateIndex.
 
469
 
 
470
        :param reload_func: A function to call if we find we are missing an
 
471
            index. Should have the form reload_func() => True if the list of
 
472
            active pack files has changed.
 
473
        """
 
474
        self._reload_func = reload_func
452
475
        self.index_to_pack = {}
453
 
        self.combined_index = CombinedGraphIndex([])
454
 
        self.data_access = _DirectPackAccess(self.index_to_pack)
 
476
        self.combined_index = CombinedGraphIndex([], reload_func=reload_func)
 
477
        self.data_access = _DirectPackAccess(self.index_to_pack,
 
478
                                             reload_func=reload_func)
455
479
        self.add_callback = None
456
480
 
457
481
    def replace_indices(self, index_to_pack, indices):
557
581
    def _extra_init(self):
558
582
        """A template hook to allow extending the constructor trivially."""
559
583
 
 
584
    def _pack_map_and_index_list(self, index_attribute):
 
585
        """Convert a list of packs to an index pack map and index list.
 
586
 
 
587
        :param index_attribute: The attribute that the desired index is found
 
588
            on.
 
589
        :return: A tuple (map, list) where map contains the dict from
 
590
            index:pack_tuple, and list contains the indices in the preferred
 
591
            access order.
 
592
        """
 
593
        indices = []
 
594
        pack_map = {}
 
595
        for pack_obj in self.packs:
 
596
            index = getattr(pack_obj, index_attribute)
 
597
            indices.append(index)
 
598
            pack_map[index] = pack_obj
 
599
        return pack_map, indices
 
600
 
 
601
    def _index_contents(self, indices, key_filter=None):
 
602
        """Get an iterable of the index contents from a pack_map.
 
603
 
 
604
        :param indices: The list of indices to query
 
605
        :param key_filter: An optional filter to limit the keys returned.
 
606
        """
 
607
        all_index = CombinedGraphIndex(indices)
 
608
        if key_filter is None:
 
609
            return all_index.iter_all_entries()
 
610
        else:
 
611
            return all_index.iter_entries(key_filter)
 
612
 
560
613
    def pack(self, pb=None):
561
614
        """Create a new pack by reading data from other packs.
562
615
 
598
651
 
599
652
    def open_pack(self):
600
653
        """Open a pack for the pack we are creating."""
601
 
        return NewPack(self._pack_collection._upload_transport,
602
 
            self._pack_collection._index_transport,
603
 
            self._pack_collection._pack_transport, upload_suffix=self.suffix,
604
 
            file_mode=self._pack_collection.repo.bzrdir._get_file_mode())
 
654
        return NewPack(self._pack_collection, upload_suffix=self.suffix,
 
655
                file_mode=self._pack_collection.repo.bzrdir._get_file_mode())
 
656
 
 
657
    def _update_pack_order(self, entries, index_to_pack_map):
 
658
        """Determine how we want our packs to be ordered.
 
659
 
 
660
        This changes the sort order of the self.packs list so that packs unused
 
661
        by 'entries' will be at the end of the list, so that future requests
 
662
        can avoid probing them.  Used packs will be at the front of the
 
663
        self.packs list, in the order of their first use in 'entries'.
 
664
 
 
665
        :param entries: A list of (index, ...) tuples
 
666
        :param index_to_pack_map: A mapping from index objects to pack objects.
 
667
        """
 
668
        packs = []
 
669
        seen_indexes = set()
 
670
        for entry in entries:
 
671
            index = entry[0]
 
672
            if index not in seen_indexes:
 
673
                packs.append(index_to_pack_map[index])
 
674
                seen_indexes.add(index)
 
675
        if len(packs) == len(self.packs):
 
676
            if 'pack' in debug.debug_flags:
 
677
                mutter('Not changing pack list, all packs used.')
 
678
            return
 
679
        seen_packs = set(packs)
 
680
        for pack in self.packs:
 
681
            if pack not in seen_packs:
 
682
                packs.append(pack)
 
683
                seen_packs.add(pack)
 
684
        if 'pack' in debug.debug_flags:
 
685
            old_names = [p.access_tuple()[1] for p in self.packs]
 
686
            new_names = [p.access_tuple()[1] for p in packs]
 
687
            mutter('Reordering packs\nfrom: %s\n  to: %s',
 
688
                   old_names, new_names)
 
689
        self.packs = packs
605
690
 
606
691
    def _copy_revision_texts(self):
607
692
        """Copy revision data to the new pack."""
611
696
        else:
612
697
            revision_keys = None
613
698
        # select revision keys
614
 
        revision_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
615
 
            self.packs, 'revision_index')[0]
616
 
        revision_nodes = self._pack_collection._index_contents(revision_index_map, revision_keys)
 
699
        revision_index_map, revision_indices = self._pack_map_and_index_list(
 
700
            'revision_index')
 
701
        revision_nodes = self._index_contents(revision_indices, revision_keys)
 
702
        revision_nodes = list(revision_nodes)
 
703
        self._update_pack_order(revision_nodes, revision_index_map)
617
704
        # copy revision keys and adjust values
618
705
        self.pb.update("Copying revision texts", 1)
619
706
        total_items, readv_group_iter = self._revision_node_readv(revision_nodes)
639
726
        # querying for keys here could introduce a bug where an inventory item
640
727
        # is missed, so do not change it to query separately without cross
641
728
        # checking like the text key check below.
642
 
        inventory_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
643
 
            self.packs, 'inventory_index')[0]
644
 
        inv_nodes = self._pack_collection._index_contents(inventory_index_map, inv_keys)
 
729
        inventory_index_map, inventory_indices = self._pack_map_and_index_list(
 
730
            'inventory_index')
 
731
        inv_nodes = self._index_contents(inventory_indices, inv_keys)
645
732
        # copy inventory keys and adjust values
646
733
        # XXX: Should be a helper function to allow different inv representation
647
734
        # at this point.
691
778
            self.new_pack.text_index, readv_group_iter, total_items))
692
779
        self._log_copied_texts()
693
780
 
694
 
    def _check_references(self):
695
 
        """Make sure our external refereneces are present."""
696
 
        external_refs = self.new_pack._external_compression_parents_of_texts()
697
 
        if external_refs:
698
 
            index = self._pack_collection.text_index.combined_index
699
 
            found_items = list(index.iter_entries(external_refs))
700
 
            if len(found_items) != len(external_refs):
701
 
                found_keys = set(k for idx, k, refs, value in found_items)
702
 
                missing_items = external_refs - found_keys
703
 
                missing_file_id, missing_revision_id = missing_items.pop()
704
 
                raise errors.RevisionNotPresent(missing_revision_id,
705
 
                                                missing_file_id)
706
 
 
707
781
    def _create_pack_from_packs(self):
708
782
        self.pb.update("Opening pack", 0, 5)
709
783
        self.new_pack = self.open_pack()
727
801
        self._copy_text_texts()
728
802
        # select signature keys
729
803
        signature_filter = self._revision_keys # same keyspace
730
 
        signature_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
731
 
            self.packs, 'signature_index')[0]
732
 
        signature_nodes = self._pack_collection._index_contents(signature_index_map,
 
804
        signature_index_map, signature_indices = self._pack_map_and_index_list(
 
805
            'signature_index')
 
806
        signature_nodes = self._index_contents(signature_indices,
733
807
            signature_filter)
734
808
        # copy signature keys and adjust values
735
809
        self.pb.update("Copying signature texts", 4)
740
814
                time.ctime(), self._pack_collection._upload_transport.base, new_pack.random_name,
741
815
                new_pack.signature_index.key_count(),
742
816
                time.time() - new_pack.start_time)
743
 
        self._check_references()
 
817
        new_pack._check_references()
744
818
        if not self._use_pack(new_pack):
745
819
            new_pack.abort()
746
820
            return None
785
859
            # linear scan up the pack
786
860
            pack_readv_requests.sort()
787
861
            # copy the data
788
 
            transport, path = index_map[index]
 
862
            pack_obj = index_map[index]
 
863
            transport, path = pack_obj.access_tuple()
789
864
            reader = pack.make_readv_reader(transport, path,
790
865
                [offset[0:2] for offset in pack_readv_requests])
791
866
            for (names, read_func), (_1, _2, (key, eol_flag)) in \
829
904
        pb.update("Copied record", record_index, total_items)
830
905
        for index, readv_vector, node_vector in readv_group_iter:
831
906
            # copy the data
832
 
            transport, path = index_map[index]
 
907
            pack_obj = index_map[index]
 
908
            transport, path = pack_obj.access_tuple()
833
909
            reader = pack.make_readv_reader(transport, path, readv_vector)
834
910
            for (names, read_func), (key, eol_flag, references) in \
835
911
                izip(reader.iter_records(), node_vector):
853
929
                record_index += 1
854
930
 
855
931
    def _get_text_nodes(self):
856
 
        text_index_map = self._pack_collection._packs_list_to_pack_map_and_index_list(
857
 
            self.packs, 'text_index')[0]
858
 
        return text_index_map, self._pack_collection._index_contents(text_index_map,
 
932
        text_index_map, text_indices = self._pack_map_and_index_list(
 
933
            'text_index')
 
934
        return text_index_map, self._index_contents(text_indices,
859
935
            self._text_filter)
860
936
 
861
937
    def _least_readv_node_readv(self, nodes):
964
1040
        # TODO: combine requests in the same index that are in ascending order.
965
1041
        return total, requests
966
1042
 
 
1043
    def open_pack(self):
 
1044
        """Open a pack for the pack we are creating."""
 
1045
        new_pack = super(OptimisingPacker, self).open_pack()
 
1046
        # Turn on the optimization flags for all the index builders.
 
1047
        new_pack.revision_index.set_optimize(for_size=True)
 
1048
        new_pack.inventory_index.set_optimize(for_size=True)
 
1049
        new_pack.text_index.set_optimize(for_size=True)
 
1050
        new_pack.signature_index.set_optimize(for_size=True)
 
1051
        return new_pack
 
1052
 
967
1053
 
968
1054
class ReconcilePacker(Packer):
969
1055
    """A packer which regenerates indices etc as it copies.
1081
1167
                    raise errors.BzrError('Mismatched key parent %r:%r' %
1082
1168
                        (key, parent_keys))
1083
1169
                parents.append(parent_key[1])
1084
 
            text_lines = split_lines(repo.texts.get_record_stream(
 
1170
            text_lines = osutils.split_lines(repo.texts.get_record_stream(
1085
1171
                [key], 'unordered', True).next().get_bytes_as('fulltext'))
1086
1172
            output_texts.add_lines(key, parent_keys, text_lines,
1087
1173
                random_id=True, check_content=False)
1088
1174
        # 5) check that nothing inserted has a reference outside the keyspace.
1089
 
        missing_text_keys = self.new_pack._external_compression_parents_of_texts()
 
1175
        missing_text_keys = self.new_pack.text_index._external_references()
1090
1176
        if missing_text_keys:
1091
 
            raise errors.BzrError('Reference to missing compression parents %r'
 
1177
            raise errors.BzrCheckError('Reference to missing compression parents %r'
1092
1178
                % (missing_text_keys,))
1093
1179
        self._log_copied_texts()
1094
1180
 
1114
1200
    """
1115
1201
 
1116
1202
    def __init__(self, repo, transport, index_transport, upload_transport,
1117
 
                 pack_transport):
 
1203
                 pack_transport, index_builder_class, index_class):
1118
1204
        """Create a new RepositoryPackCollection.
1119
1205
 
1120
1206
        :param transport: Addresses the repository base directory 
1123
1209
        :param upload_transport: Addresses the directory into which packs are written
1124
1210
            while they're being created.
1125
1211
        :param pack_transport: Addresses the directory of existing complete packs.
 
1212
        :param index_builder_class: The index builder class to use.
 
1213
        :param index_class: The index class to use.
1126
1214
        """
1127
1215
        self.repo = repo
1128
1216
        self.transport = transport
1129
1217
        self._index_transport = index_transport
1130
1218
        self._upload_transport = upload_transport
1131
1219
        self._pack_transport = pack_transport
 
1220
        self._index_builder_class = index_builder_class
 
1221
        self._index_class = index_class
1132
1222
        self._suffix_offsets = {'.rix': 0, '.iix': 1, '.tix': 2, '.six': 3}
1133
1223
        self.packs = []
1134
1224
        # name:Pack mapping
1138
1228
        # when a pack is being created by this object, the state of that pack.
1139
1229
        self._new_pack = None
1140
1230
        # aggregated revision index data
1141
 
        self.revision_index = AggregateIndex()
1142
 
        self.inventory_index = AggregateIndex()
1143
 
        self.text_index = AggregateIndex()
1144
 
        self.signature_index = AggregateIndex()
 
1231
        self.revision_index = AggregateIndex(self.reload_pack_names)
 
1232
        self.inventory_index = AggregateIndex(self.reload_pack_names)
 
1233
        self.text_index = AggregateIndex(self.reload_pack_names)
 
1234
        self.signature_index = AggregateIndex(self.reload_pack_names)
1145
1235
 
1146
1236
    def add_pack_to_memory(self, pack):
1147
1237
        """Make a Pack object available to the repository to satisfy queries.
1192
1282
            return False
1193
1283
        # XXX: the following may want to be a class, to pack with a given
1194
1284
        # policy.
1195
 
        mutter('Auto-packing repository %s, which has %d pack files, '
1196
 
            'containing %d revisions into %d packs.', self, total_packs,
1197
 
            total_revisions, self._max_pack_count(total_revisions))
1198
1285
        # determine which packs need changing
1199
1286
        pack_distribution = self.pack_distribution(total_revisions)
1200
1287
        existing_packs = []
1215
1302
            existing_packs.append((revision_count, pack))
1216
1303
        pack_operations = self.plan_autopack_combinations(
1217
1304
            existing_packs, pack_distribution)
 
1305
        num_new_packs = len(pack_operations)
 
1306
        num_old_packs = sum([len(po[1]) for po in pack_operations])
 
1307
        num_revs_affected = sum([po[0] for po in pack_operations])
 
1308
        mutter('Auto-packing repository %s, which has %d pack files, '
 
1309
            'containing %d revisions. Packing %d files into %d affecting %d'
 
1310
            ' revisions', self, total_packs, total_revisions, num_old_packs,
 
1311
            num_new_packs, num_revs_affected)
1218
1312
        self._execute_pack_operations(pack_operations)
1219
1313
        return True
1220
1314
 
1285
1379
        # plan out what packs to keep, and what to reorganise
1286
1380
        while len(existing_packs):
1287
1381
            # take the largest pack, and if its less than the head of the
1288
 
            # distribution chart we will include its contents in the new pack for
1289
 
            # that position. If its larger, we remove its size from the
 
1382
            # distribution chart we will include its contents in the new pack
 
1383
            # for that position. If its larger, we remove its size from the
1290
1384
            # distribution chart
1291
1385
            next_pack_rev_count, next_pack = existing_packs.pop(0)
1292
1386
            if next_pack_rev_count >= pack_distribution[0]:
1309
1403
                    # this pack is used up, shift left.
1310
1404
                    del pack_distribution[0]
1311
1405
                    pack_operations.append([0, []])
1312
 
        
1313
 
        return pack_operations
 
1406
        # Now that we know which pack files we want to move, shove them all
 
1407
        # into a single pack file.
 
1408
        final_rev_count = 0
 
1409
        final_pack_list = []
 
1410
        for num_revs, pack_files in pack_operations:
 
1411
            final_rev_count += num_revs
 
1412
            final_pack_list.extend(pack_files)
 
1413
        if len(final_pack_list) == 1:
 
1414
            raise AssertionError('We somehow generated an autopack with a'
 
1415
                ' single pack file being moved.')
 
1416
            return []
 
1417
        return [[final_rev_count, final_pack_list]]
1314
1418
 
1315
1419
    def ensure_loaded(self):
1316
1420
        # NB: if you see an assertion error here, its probably access against
1369
1473
        detect updates from others during our write operation.
1370
1474
        :return: An iterator of the index contents.
1371
1475
        """
1372
 
        return GraphIndex(self.transport, 'pack-names', None
 
1476
        return self._index_class(self.transport, 'pack-names', None
1373
1477
                ).iter_all_entries()
1374
1478
 
1375
1479
    def _make_index(self, name, suffix):
1376
1480
        size_offset = self._suffix_offsets[suffix]
1377
1481
        index_name = name + suffix
1378
1482
        index_size = self._names[name][size_offset]
1379
 
        return GraphIndex(
 
1483
        return self._index_class(
1380
1484
            self._index_transport, index_name, index_size)
1381
1485
 
1382
1486
    def _max_pack_count(self, total_revisions):
1448
1552
        self._names.pop(pack.name)
1449
1553
        self._packs_by_name.pop(pack.name)
1450
1554
        self._remove_pack_indices(pack)
 
1555
        self.packs.remove(pack)
1451
1556
 
1452
1557
    def _remove_pack_indices(self, pack):
1453
1558
        """Remove the indices for pack from the aggregated indices."""
1477
1582
        self._packs_by_name = {}
1478
1583
        self._packs_at_load = None
1479
1584
 
1480
 
    def _make_index_map(self, index_suffix):
1481
 
        """Return information on existing indices.
1482
 
 
1483
 
        :param suffix: Index suffix added to pack name.
1484
 
 
1485
 
        :returns: (pack_map, indices) where indices is a list of GraphIndex 
1486
 
        objects, and pack_map is a mapping from those objects to the 
1487
 
        pack tuple they describe.
1488
 
        """
1489
 
        # TODO: stop using this; it creates new indices unnecessarily.
1490
 
        self.ensure_loaded()
1491
 
        suffix_map = {'.rix': 'revision_index',
1492
 
            '.six': 'signature_index',
1493
 
            '.iix': 'inventory_index',
1494
 
            '.tix': 'text_index',
1495
 
        }
1496
 
        return self._packs_list_to_pack_map_and_index_list(self.all_packs(),
1497
 
            suffix_map[index_suffix])
1498
 
 
1499
 
    def _packs_list_to_pack_map_and_index_list(self, packs, index_attribute):
1500
 
        """Convert a list of packs to an index pack map and index list.
1501
 
 
1502
 
        :param packs: The packs list to process.
1503
 
        :param index_attribute: The attribute that the desired index is found
1504
 
            on.
1505
 
        :return: A tuple (map, list) where map contains the dict from
1506
 
            index:pack_tuple, and lsit contains the indices in the same order
1507
 
            as the packs list.
1508
 
        """
1509
 
        indices = []
1510
 
        pack_map = {}
1511
 
        for pack in packs:
1512
 
            index = getattr(pack, index_attribute)
1513
 
            indices.append(index)
1514
 
            pack_map[index] = (pack.pack_transport, pack.file_name())
1515
 
        return pack_map, indices
1516
 
 
1517
 
    def _index_contents(self, pack_map, key_filter=None):
1518
 
        """Get an iterable of the index contents from a pack_map.
1519
 
 
1520
 
        :param pack_map: A map from indices to pack details.
1521
 
        :param key_filter: An optional filter to limit the
1522
 
            keys returned.
1523
 
        """
1524
 
        indices = [index for index in pack_map.iterkeys()]
1525
 
        all_index = CombinedGraphIndex(indices)
1526
 
        if key_filter is None:
1527
 
            return all_index.iter_all_entries()
1528
 
        else:
1529
 
            return all_index.iter_entries(key_filter)
1530
 
 
1531
1585
    def _unlock_names(self):
1532
1586
        """Release the mutex around the pack-names index."""
1533
1587
        self.repo.control_files.unlock()
1534
1588
 
1535
 
    def _save_pack_names(self, clear_obsolete_packs=False):
1536
 
        """Save the list of packs.
1537
 
 
1538
 
        This will take out the mutex around the pack names list for the
1539
 
        duration of the method call. If concurrent updates have been made, a
1540
 
        three-way merge between the current list and the current in memory list
1541
 
        is performed.
1542
 
 
1543
 
        :param clear_obsolete_packs: If True, clear out the contents of the
1544
 
            obsolete_packs directory.
1545
 
        """
1546
 
        self.lock_names()
1547
 
        try:
1548
 
            builder = GraphIndexBuilder()
1549
 
            # load the disk nodes across
1550
 
            disk_nodes = set()
1551
 
            for index, key, value in self._iter_disk_pack_index():
1552
 
                disk_nodes.add((key, value))
1553
 
            # do a two-way diff against our original content
1554
 
            current_nodes = set()
1555
 
            for name, sizes in self._names.iteritems():
1556
 
                current_nodes.add(
1557
 
                    ((name, ), ' '.join(str(size) for size in sizes)))
1558
 
            deleted_nodes = self._packs_at_load - current_nodes
1559
 
            new_nodes = current_nodes - self._packs_at_load
1560
 
            disk_nodes.difference_update(deleted_nodes)
1561
 
            disk_nodes.update(new_nodes)
1562
 
            # TODO: handle same-name, index-size-changes here - 
1563
 
            # e.g. use the value from disk, not ours, *unless* we're the one
1564
 
            # changing it.
1565
 
            for key, value in disk_nodes:
1566
 
                builder.add_node(key, value)
1567
 
            self.transport.put_file('pack-names', builder.finish(),
1568
 
                mode=self.repo.bzrdir._get_file_mode())
1569
 
            # move the baseline forward
1570
 
            self._packs_at_load = disk_nodes
1571
 
            if clear_obsolete_packs:
1572
 
                self._clear_obsolete_packs()
1573
 
        finally:
1574
 
            self._unlock_names()
1575
 
        # synchronise the memory packs list with what we just wrote:
 
1589
    def _diff_pack_names(self):
 
1590
        """Read the pack names from disk, and compare it to the one in memory.
 
1591
 
 
1592
        :return: (disk_nodes, deleted_nodes, new_nodes)
 
1593
            disk_nodes    The final set of nodes that should be referenced
 
1594
            deleted_nodes Nodes which have been removed from when we started
 
1595
            new_nodes     Nodes that are newly introduced
 
1596
        """
 
1597
        # load the disk nodes across
 
1598
        disk_nodes = set()
 
1599
        for index, key, value in self._iter_disk_pack_index():
 
1600
            disk_nodes.add((key, value))
 
1601
 
 
1602
        # do a two-way diff against our original content
 
1603
        current_nodes = set()
 
1604
        for name, sizes in self._names.iteritems():
 
1605
            current_nodes.add(
 
1606
                ((name, ), ' '.join(str(size) for size in sizes)))
 
1607
 
 
1608
        # Packs no longer present in the repository, which were present when we
 
1609
        # locked the repository
 
1610
        deleted_nodes = self._packs_at_load - current_nodes
 
1611
        # Packs which this process is adding
 
1612
        new_nodes = current_nodes - self._packs_at_load
 
1613
 
 
1614
        # Update the disk_nodes set to include the ones we are adding, and
 
1615
        # remove the ones which were removed by someone else
 
1616
        disk_nodes.difference_update(deleted_nodes)
 
1617
        disk_nodes.update(new_nodes)
 
1618
 
 
1619
        return disk_nodes, deleted_nodes, new_nodes
 
1620
 
 
1621
    def _syncronize_pack_names_from_disk_nodes(self, disk_nodes):
 
1622
        """Given the correct set of pack files, update our saved info.
 
1623
 
 
1624
        :return: (removed, added, modified)
 
1625
            removed     pack names removed from self._names
 
1626
            added       pack names added to self._names
 
1627
            modified    pack names that had changed value
 
1628
        """
 
1629
        removed = []
 
1630
        added = []
 
1631
        modified = []
 
1632
        ## self._packs_at_load = disk_nodes
1576
1633
        new_names = dict(disk_nodes)
1577
1634
        # drop no longer present nodes
1578
1635
        for pack in self.all_packs():
1579
1636
            if (pack.name,) not in new_names:
 
1637
                removed.append(pack.name)
1580
1638
                self._remove_pack_from_memory(pack)
1581
1639
        # add new nodes/refresh existing ones
1582
1640
        for key, value in disk_nodes:
1596
1654
                    self._remove_pack_from_memory(self.get_pack_by_name(name))
1597
1655
                    self._names[name] = sizes
1598
1656
                    self.get_pack_by_name(name)
 
1657
                    modified.append(name)
1599
1658
            else:
1600
1659
                # new
1601
1660
                self._names[name] = sizes
1602
1661
                self.get_pack_by_name(name)
 
1662
                added.append(name)
 
1663
        return removed, added, modified
 
1664
 
 
1665
    def _save_pack_names(self, clear_obsolete_packs=False):
 
1666
        """Save the list of packs.
 
1667
 
 
1668
        This will take out the mutex around the pack names list for the
 
1669
        duration of the method call. If concurrent updates have been made, a
 
1670
        three-way merge between the current list and the current in memory list
 
1671
        is performed.
 
1672
 
 
1673
        :param clear_obsolete_packs: If True, clear out the contents of the
 
1674
            obsolete_packs directory.
 
1675
        """
 
1676
        self.lock_names()
 
1677
        try:
 
1678
            builder = self._index_builder_class()
 
1679
            disk_nodes, deleted_nodes, new_nodes = self._diff_pack_names()
 
1680
            # TODO: handle same-name, index-size-changes here - 
 
1681
            # e.g. use the value from disk, not ours, *unless* we're the one
 
1682
            # changing it.
 
1683
            for key, value in disk_nodes:
 
1684
                builder.add_node(key, value)
 
1685
            self.transport.put_file('pack-names', builder.finish(),
 
1686
                mode=self.repo.bzrdir._get_file_mode())
 
1687
            # move the baseline forward
 
1688
            self._packs_at_load = disk_nodes
 
1689
            if clear_obsolete_packs:
 
1690
                self._clear_obsolete_packs()
 
1691
        finally:
 
1692
            self._unlock_names()
 
1693
        # synchronise the memory packs list with what we just wrote:
 
1694
        self._syncronize_pack_names_from_disk_nodes(disk_nodes)
 
1695
 
 
1696
    def reload_pack_names(self):
 
1697
        """Sync our pack listing with what is present in the repository.
 
1698
 
 
1699
        This should be called when we find out that something we thought was
 
1700
        present is now missing. This happens when another process re-packs the
 
1701
        repository, etc.
 
1702
        """
 
1703
        # This is functionally similar to _save_pack_names, but we don't write
 
1704
        # out the new value.
 
1705
        disk_nodes, _, _ = self._diff_pack_names()
 
1706
        self._packs_at_load = disk_nodes
 
1707
        (removed, added,
 
1708
         modified) = self._syncronize_pack_names_from_disk_nodes(disk_nodes)
 
1709
        if removed or added or modified:
 
1710
            return True
 
1711
        return False
1603
1712
 
1604
1713
    def _clear_obsolete_packs(self):
1605
1714
        """Delete everything from the obsolete-packs directory.
1615
1724
        # Do not permit preparation for writing if we're not in a 'write lock'.
1616
1725
        if not self.repo.is_write_locked():
1617
1726
            raise errors.NotWriteLocked(self)
1618
 
        self._new_pack = NewPack(self._upload_transport, self._index_transport,
1619
 
            self._pack_transport, upload_suffix='.pack',
 
1727
        self._new_pack = NewPack(self, upload_suffix='.pack',
1620
1728
            file_mode=self.repo.bzrdir._get_file_mode())
1621
1729
        # allow writing: queue writes to a new index
1622
1730
        self.revision_index.add_writable_index(self._new_pack.revision_index,
1637
1745
        # FIXME: just drop the transient index.
1638
1746
        # forget what names there are
1639
1747
        if self._new_pack is not None:
1640
 
            self._new_pack.abort()
1641
 
            self._remove_pack_indices(self._new_pack)
1642
 
            self._new_pack = None
 
1748
            try:
 
1749
                self._new_pack.abort()
 
1750
            finally:
 
1751
                # XXX: If we aborted while in the middle of finishing the write
 
1752
                # group, _remove_pack_indices can fail because the indexes are
 
1753
                # already gone.  If they're not there we shouldn't fail in this
 
1754
                # case.  -- mbp 20081113
 
1755
                self._remove_pack_indices(self._new_pack)
 
1756
                self._new_pack = None
1643
1757
        self.repo._text_knit = None
1644
1758
 
1645
1759
    def _commit_write_group(self):
1692
1806
        self._pack_collection = RepositoryPackCollection(self, self._transport,
1693
1807
            index_transport,
1694
1808
            self._transport.clone('upload'),
1695
 
            self._transport.clone('packs'))
 
1809
            self._transport.clone('packs'),
 
1810
            _format.index_builder_class,
 
1811
            _format.index_class)
1696
1812
        self.inventories = KnitVersionedFiles(
1697
1813
            _KnitGraphIndex(self._pack_collection.inventory_index.combined_index,
1698
1814
                add_callback=self._pack_collection.inventory_index.add_callback,
1728
1844
        self._reconcile_does_inventory_gc = True
1729
1845
        self._reconcile_fixes_text_parents = True
1730
1846
        self._reconcile_backsup_inventory = False
1731
 
        self._fetch_order = 'unsorted'
 
1847
        self._fetch_order = 'unordered'
1732
1848
 
1733
1849
    def _warn_if_deprecated(self):
1734
 
        # This class isn't deprecated
1735
 
        pass
 
1850
        # This class isn't deprecated, but one sub-format is
 
1851
        if isinstance(self._format, RepositoryFormatKnitPack5RichRootBroken):
 
1852
            from bzrlib import repository
 
1853
            if repository._deprecation_warning_done:
 
1854
                return
 
1855
            repository._deprecation_warning_done = True
 
1856
            warning("Format %s for %s is deprecated - please use"
 
1857
                    " 'bzr upgrade --1.6.1-rich-root'"
 
1858
                    % (self._format, self.bzrdir.transport.base))
1736
1859
 
1737
1860
    def _abort_write_group(self):
1738
1861
        self._pack_collection._abort_write_group()
1819
1942
            raise errors.ReadOnlyError(self)
1820
1943
        self._write_lock_count += 1
1821
1944
        if self._write_lock_count == 1:
1822
 
            from bzrlib import transactions
1823
1945
            self._transaction = transactions.WriteTransaction()
1824
1946
            for repo in self._fallback_repositories:
1825
1947
                # Writes don't affect fallback repos
1910
2032
    _serializer = None
1911
2033
    # External references are not supported in pack repositories yet.
1912
2034
    supports_external_lookups = False
1913
 
 
1914
 
    def initialize(self, a_bzrdir, shared=False):
1915
 
        """Create a pack based repository.
1916
 
 
1917
 
        :param a_bzrdir: bzrdir to contain the new repository; must already
1918
 
            be initialized.
1919
 
        :param shared: If true the repository will be initialized as a shared
1920
 
                       repository.
1921
 
        """
1922
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
1923
 
        dirs = ['indices', 'obsolete_packs', 'packs', 'upload']
1924
 
        builder = GraphIndexBuilder()
1925
 
        files = [('pack-names', builder.finish())]
1926
 
        utf8_files = [('format', self.get_format_string())]
1927
 
        
1928
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
1929
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
1930
 
 
1931
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
1932
 
        """See RepositoryFormat.open().
1933
 
        
1934
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
1935
 
                                    repository at a slightly different url
1936
 
                                    than normal. I.e. during 'upgrade'.
1937
 
        """
1938
 
        if not _found:
1939
 
            format = RepositoryFormat.find_format(a_bzrdir)
1940
 
        if _override_transport is not None:
1941
 
            repo_transport = _override_transport
1942
 
        else:
1943
 
            repo_transport = a_bzrdir.get_repository_transport(None)
1944
 
        control_files = lockable_files.LockableFiles(repo_transport,
1945
 
                                'lock', lockdir.LockDir)
1946
 
        return self.repository_class(_format=self,
1947
 
                              a_bzrdir=a_bzrdir,
1948
 
                              control_files=control_files,
1949
 
                              _commit_builder_class=self._commit_builder_class,
1950
 
                              _serializer=self._serializer)
1951
 
 
1952
 
 
1953
 
class RepositoryFormatKnitPack1(RepositoryFormatPack):
1954
 
    """A no-subtrees parameterized Pack repository.
1955
 
 
1956
 
    This format was introduced in 0.92.
1957
 
    """
1958
 
 
1959
 
    repository_class = KnitPackRepository
1960
 
    _commit_builder_class = PackCommitBuilder
1961
 
    _serializer = xml5.serializer_v5
1962
 
 
1963
 
    def _get_matching_bzrdir(self):
1964
 
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
1965
 
 
1966
 
    def _ignore_setting_bzrdir(self, format):
1967
 
        pass
1968
 
 
1969
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
1970
 
 
1971
 
    def get_format_string(self):
1972
 
        """See RepositoryFormat.get_format_string()."""
1973
 
        return "Bazaar pack repository format 1 (needs bzr 0.92)\n"
1974
 
 
1975
 
    def get_format_description(self):
1976
 
        """See RepositoryFormat.get_format_description()."""
1977
 
        return "Packs containing knits without subtree support"
1978
 
 
1979
 
    def check_conversion_target(self, target_format):
1980
 
        pass
1981
 
 
1982
 
 
1983
 
class RepositoryFormatPack(MetaDirRepositoryFormat):
1984
 
    """Format logic for pack structured repositories.
1985
 
 
1986
 
    This repository format has:
1987
 
     - a list of packs in pack-names
1988
 
     - packs in packs/NAME.pack
1989
 
     - indices in indices/NAME.{iix,six,tix,rix}
1990
 
     - knit deltas in the packs, knit indices mapped to the indices.
1991
 
     - thunk objects to support the knits programming API.
1992
 
     - a format marker of its own
1993
 
     - an optional 'shared-storage' flag
1994
 
     - an optional 'no-working-trees' flag
1995
 
     - a LockDir lock
1996
 
    """
1997
 
 
1998
 
    # Set this attribute in derived classes to control the repository class
1999
 
    # created by open and initialize.
2000
 
    repository_class = None
2001
 
    # Set this attribute in derived classes to control the
2002
 
    # _commit_builder_class that the repository objects will have passed to
2003
 
    # their constructor.
2004
 
    _commit_builder_class = None
2005
 
    # Set this attribute in derived clases to control the _serializer that the
2006
 
    # repository objects will have passed to their constructor.
2007
 
    _serializer = None
2008
 
    # External references are not supported in pack repositories yet.
2009
 
    supports_external_lookups = False
2010
 
 
2011
 
    def initialize(self, a_bzrdir, shared=False):
2012
 
        """Create a pack based repository.
2013
 
 
2014
 
        :param a_bzrdir: bzrdir to contain the new repository; must already
2015
 
            be initialized.
2016
 
        :param shared: If true the repository will be initialized as a shared
2017
 
                       repository.
2018
 
        """
2019
 
        mutter('creating repository in %s.', a_bzrdir.transport.base)
2020
 
        dirs = ['indices', 'obsolete_packs', 'packs', 'upload']
2021
 
        builder = GraphIndexBuilder()
2022
 
        files = [('pack-names', builder.finish())]
2023
 
        utf8_files = [('format', self.get_format_string())]
2024
 
        
2025
 
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
2026
 
        return self.open(a_bzrdir=a_bzrdir, _found=True)
2027
 
 
2028
 
    def open(self, a_bzrdir, _found=False, _override_transport=None):
2029
 
        """See RepositoryFormat.open().
2030
 
        
2031
 
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
2032
 
                                    repository at a slightly different url
2033
 
                                    than normal. I.e. during 'upgrade'.
2034
 
        """
2035
 
        if not _found:
2036
 
            format = RepositoryFormat.find_format(a_bzrdir)
2037
 
        if _override_transport is not None:
2038
 
            repo_transport = _override_transport
2039
 
        else:
2040
 
            repo_transport = a_bzrdir.get_repository_transport(None)
2041
 
        control_files = lockable_files.LockableFiles(repo_transport,
2042
 
                                'lock', lockdir.LockDir)
2043
 
        return self.repository_class(_format=self,
2044
 
                              a_bzrdir=a_bzrdir,
2045
 
                              control_files=control_files,
2046
 
                              _commit_builder_class=self._commit_builder_class,
2047
 
                              _serializer=self._serializer)
2048
 
 
2049
 
 
2050
 
class RepositoryFormatKnitPack1(RepositoryFormatPack):
2051
 
    """A no-subtrees parameterized Pack repository.
2052
 
 
2053
 
    This format was introduced in 0.92.
2054
 
    """
2055
 
 
2056
 
    repository_class = KnitPackRepository
2057
 
    _commit_builder_class = PackCommitBuilder
2058
 
    _serializer = xml5.serializer_v5
 
2035
    # What index classes to use
 
2036
    index_builder_class = None
 
2037
    index_class = None
 
2038
 
 
2039
    def initialize(self, a_bzrdir, shared=False):
 
2040
        """Create a pack based repository.
 
2041
 
 
2042
        :param a_bzrdir: bzrdir to contain the new repository; must already
 
2043
            be initialized.
 
2044
        :param shared: If true the repository will be initialized as a shared
 
2045
                       repository.
 
2046
        """
 
2047
        mutter('creating repository in %s.', a_bzrdir.transport.base)
 
2048
        dirs = ['indices', 'obsolete_packs', 'packs', 'upload']
 
2049
        builder = self.index_builder_class()
 
2050
        files = [('pack-names', builder.finish())]
 
2051
        utf8_files = [('format', self.get_format_string())]
 
2052
        
 
2053
        self._upload_blank_content(a_bzrdir, dirs, files, utf8_files, shared)
 
2054
        return self.open(a_bzrdir=a_bzrdir, _found=True)
 
2055
 
 
2056
    def open(self, a_bzrdir, _found=False, _override_transport=None):
 
2057
        """See RepositoryFormat.open().
 
2058
        
 
2059
        :param _override_transport: INTERNAL USE ONLY. Allows opening the
 
2060
                                    repository at a slightly different url
 
2061
                                    than normal. I.e. during 'upgrade'.
 
2062
        """
 
2063
        if not _found:
 
2064
            format = RepositoryFormat.find_format(a_bzrdir)
 
2065
        if _override_transport is not None:
 
2066
            repo_transport = _override_transport
 
2067
        else:
 
2068
            repo_transport = a_bzrdir.get_repository_transport(None)
 
2069
        control_files = lockable_files.LockableFiles(repo_transport,
 
2070
                                'lock', lockdir.LockDir)
 
2071
        return self.repository_class(_format=self,
 
2072
                              a_bzrdir=a_bzrdir,
 
2073
                              control_files=control_files,
 
2074
                              _commit_builder_class=self._commit_builder_class,
 
2075
                              _serializer=self._serializer)
 
2076
 
 
2077
 
 
2078
class RepositoryFormatKnitPack1(RepositoryFormatPack):
 
2079
    """A no-subtrees parameterized Pack repository.
 
2080
 
 
2081
    This format was introduced in 0.92.
 
2082
    """
 
2083
 
 
2084
    repository_class = KnitPackRepository
 
2085
    _commit_builder_class = PackCommitBuilder
 
2086
    @property
 
2087
    def _serializer(self):
 
2088
        return xml5.serializer_v5
 
2089
    # What index classes to use
 
2090
    index_builder_class = InMemoryGraphIndex
 
2091
    index_class = GraphIndex
2059
2092
 
2060
2093
    def _get_matching_bzrdir(self):
2061
2094
        return bzrdir.format_registry.make_bzrdir('pack-0.92')
2091
2124
    _commit_builder_class = PackRootCommitBuilder
2092
2125
    rich_root_data = True
2093
2126
    supports_tree_reference = True
2094
 
    _serializer = xml7.serializer_v7
 
2127
    @property
 
2128
    def _serializer(self):
 
2129
        return xml7.serializer_v7
 
2130
    # What index classes to use
 
2131
    index_builder_class = InMemoryGraphIndex
 
2132
    index_class = GraphIndex
2095
2133
 
2096
2134
    def _get_matching_bzrdir(self):
2097
2135
        return bzrdir.format_registry.make_bzrdir(
2132
2170
    _commit_builder_class = PackRootCommitBuilder
2133
2171
    rich_root_data = True
2134
2172
    supports_tree_reference = False
2135
 
    _serializer = xml6.serializer_v6
 
2173
    @property
 
2174
    def _serializer(self):
 
2175
        return xml6.serializer_v6
 
2176
    # What index classes to use
 
2177
    index_builder_class = InMemoryGraphIndex
 
2178
    index_class = GraphIndex
2136
2179
 
2137
2180
    def _get_matching_bzrdir(self):
2138
2181
        return bzrdir.format_registry.make_bzrdir(
2169
2212
 
2170
2213
    repository_class = KnitPackRepository
2171
2214
    _commit_builder_class = PackCommitBuilder
2172
 
    _serializer = xml5.serializer_v5
2173
2215
    supports_external_lookups = True
 
2216
    # What index classes to use
 
2217
    index_builder_class = InMemoryGraphIndex
 
2218
    index_class = GraphIndex
 
2219
 
 
2220
    @property
 
2221
    def _serializer(self):
 
2222
        return xml5.serializer_v5
2174
2223
 
2175
2224
    def _get_matching_bzrdir(self):
2176
 
        return bzrdir.format_registry.make_bzrdir('development1')
 
2225
        return bzrdir.format_registry.make_bzrdir('1.6')
2177
2226
 
2178
2227
    def _ignore_setting_bzrdir(self, format):
2179
2228
        pass
2186
2235
 
2187
2236
    def get_format_description(self):
2188
2237
        """See RepositoryFormat.get_format_description()."""
2189
 
        return self.__doc__
 
2238
        return "Packs 5 (adds stacking support, requires bzr 1.6)"
2190
2239
 
2191
2240
    def check_conversion_target(self, target_format):
2192
2241
        pass
2193
2242
 
2194
2243
 
2195
2244
class RepositoryFormatKnitPack5RichRoot(RepositoryFormatPack):
2196
 
    """A repository with subtrees and external references.
 
2245
    """A repository with rich roots and stacking.
 
2246
 
 
2247
    New in release 1.6.1.
 
2248
 
 
2249
    Supports stacking on other repositories, allowing data to be accessed
 
2250
    without being stored locally.
 
2251
    """
 
2252
 
 
2253
    repository_class = KnitPackRepository
 
2254
    _commit_builder_class = PackRootCommitBuilder
 
2255
    rich_root_data = True
 
2256
    supports_tree_reference = False # no subtrees
 
2257
    supports_external_lookups = True
 
2258
    # What index classes to use
 
2259
    index_builder_class = InMemoryGraphIndex
 
2260
    index_class = GraphIndex
 
2261
 
 
2262
    @property
 
2263
    def _serializer(self):
 
2264
        return xml6.serializer_v6
 
2265
 
 
2266
    def _get_matching_bzrdir(self):
 
2267
        return bzrdir.format_registry.make_bzrdir(
 
2268
            '1.6.1-rich-root')
 
2269
 
 
2270
    def _ignore_setting_bzrdir(self, format):
 
2271
        pass
 
2272
 
 
2273
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2274
 
 
2275
    def check_conversion_target(self, target_format):
 
2276
        if not target_format.rich_root_data:
 
2277
            raise errors.BadConversionTarget(
 
2278
                'Does not support rich root data.', target_format)
 
2279
 
 
2280
    def get_format_string(self):
 
2281
        """See RepositoryFormat.get_format_string()."""
 
2282
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n"
 
2283
 
 
2284
    def get_format_description(self):
 
2285
        return "Packs 5 rich-root (adds stacking support, requires bzr 1.6.1)"
 
2286
 
 
2287
 
 
2288
class RepositoryFormatKnitPack5RichRootBroken(RepositoryFormatPack):
 
2289
    """A repository with rich roots and external references.
2197
2290
 
2198
2291
    New in release 1.6.
2199
2292
 
2200
2293
    Supports external lookups, which results in non-truncated ghosts after
2201
2294
    reconcile compared to pack-0.92 formats.
 
2295
 
 
2296
    This format was deprecated because the serializer it uses accidentally
 
2297
    supported subtrees, when the format was not intended to. This meant that
 
2298
    someone could accidentally fetch from an incorrect repository.
2202
2299
    """
2203
2300
 
2204
2301
    repository_class = KnitPackRepository
2205
2302
    _commit_builder_class = PackRootCommitBuilder
2206
2303
    rich_root_data = True
2207
2304
    supports_tree_reference = False # no subtrees
2208
 
    _serializer = xml7.serializer_v7
2209
2305
 
2210
2306
    supports_external_lookups = True
 
2307
    # What index classes to use
 
2308
    index_builder_class = InMemoryGraphIndex
 
2309
    index_class = GraphIndex
 
2310
 
 
2311
    @property
 
2312
    def _serializer(self):
 
2313
        return xml7.serializer_v7
2211
2314
 
2212
2315
    def _get_matching_bzrdir(self):
2213
 
        return bzrdir.format_registry.make_bzrdir(
2214
 
            'development1-subtree')
 
2316
        matching = bzrdir.format_registry.make_bzrdir(
 
2317
            '1.6.1-rich-root')
 
2318
        matching.repository_format = self
 
2319
        return matching
2215
2320
 
2216
2321
    def _ignore_setting_bzrdir(self, format):
2217
2322
        pass
2222
2327
        if not target_format.rich_root_data:
2223
2328
            raise errors.BadConversionTarget(
2224
2329
                'Does not support rich root data.', target_format)
2225
 
            
 
2330
 
2226
2331
    def get_format_string(self):
2227
2332
        """See RepositoryFormat.get_format_string()."""
2228
2333
        return "Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n"
2229
2334
 
2230
2335
    def get_format_description(self):
 
2336
        return ("Packs 5 rich-root (adds stacking support, requires bzr 1.6)"
 
2337
                " (deprecated)")
 
2338
 
 
2339
 
 
2340
class RepositoryFormatKnitPack6(RepositoryFormatPack):
 
2341
    """A repository with stacking and btree indexes,
 
2342
    without rich roots or subtrees.
 
2343
 
 
2344
    This is equivalent to pack-1.6 with B+Tree indices.
 
2345
    """
 
2346
 
 
2347
    repository_class = KnitPackRepository
 
2348
    _commit_builder_class = PackCommitBuilder
 
2349
    supports_external_lookups = True
 
2350
    # What index classes to use
 
2351
    index_builder_class = BTreeBuilder
 
2352
    index_class = BTreeGraphIndex
 
2353
 
 
2354
    @property
 
2355
    def _serializer(self):
 
2356
        return xml5.serializer_v5
 
2357
 
 
2358
    def _get_matching_bzrdir(self):
 
2359
        return bzrdir.format_registry.make_bzrdir('1.9')
 
2360
 
 
2361
    def _ignore_setting_bzrdir(self, format):
 
2362
        pass
 
2363
 
 
2364
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2365
 
 
2366
    def get_format_string(self):
 
2367
        """See RepositoryFormat.get_format_string()."""
 
2368
        return "Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n"
 
2369
 
 
2370
    def get_format_description(self):
2231
2371
        """See RepositoryFormat.get_format_description()."""
2232
 
        return self.__doc__
2233
 
 
2234
 
 
2235
 
class RepositoryFormatPackDevelopment0(RepositoryFormatPack):
 
2372
        return "Packs 6 (uses btree indexes, requires bzr 1.9)"
 
2373
 
 
2374
    def check_conversion_target(self, target_format):
 
2375
        pass
 
2376
 
 
2377
 
 
2378
class RepositoryFormatKnitPack6RichRoot(RepositoryFormatPack):
 
2379
    """A repository with rich roots, no subtrees, stacking and btree indexes.
 
2380
 
 
2381
    1.6-rich-root with B+Tree indices.
 
2382
    """
 
2383
 
 
2384
    repository_class = KnitPackRepository
 
2385
    _commit_builder_class = PackRootCommitBuilder
 
2386
    rich_root_data = True
 
2387
    supports_tree_reference = False # no subtrees
 
2388
    supports_external_lookups = True
 
2389
    # What index classes to use
 
2390
    index_builder_class = BTreeBuilder
 
2391
    index_class = BTreeGraphIndex
 
2392
 
 
2393
    @property
 
2394
    def _serializer(self):
 
2395
        return xml6.serializer_v6
 
2396
 
 
2397
    def _get_matching_bzrdir(self):
 
2398
        return bzrdir.format_registry.make_bzrdir(
 
2399
            '1.9-rich-root')
 
2400
 
 
2401
    def _ignore_setting_bzrdir(self, format):
 
2402
        pass
 
2403
 
 
2404
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2405
 
 
2406
    def check_conversion_target(self, target_format):
 
2407
        if not target_format.rich_root_data:
 
2408
            raise errors.BadConversionTarget(
 
2409
                'Does not support rich root data.', target_format)
 
2410
 
 
2411
    def get_format_string(self):
 
2412
        """See RepositoryFormat.get_format_string()."""
 
2413
        return "Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n"
 
2414
 
 
2415
    def get_format_description(self):
 
2416
        return "Packs 6 rich-root (uses btree indexes, requires bzr 1.9)"
 
2417
 
 
2418
 
 
2419
class RepositoryFormatPackDevelopment2(RepositoryFormatPack):
2236
2420
    """A no-subtrees development repository.
2237
2421
 
2238
 
    This format should be retained until the second release after bzr 1.0.
 
2422
    This format should be retained until the second release after bzr 1.7.
2239
2423
 
2240
 
    No changes to the disk behaviour from pack-0.92.
 
2424
    This is pack-1.6.1 with B+Tree indices.
2241
2425
    """
2242
2426
 
2243
2427
    repository_class = KnitPackRepository
2244
2428
    _commit_builder_class = PackCommitBuilder
2245
 
    _serializer = xml5.serializer_v5
 
2429
    supports_external_lookups = True
 
2430
    # What index classes to use
 
2431
    index_builder_class = BTreeBuilder
 
2432
    index_class = BTreeGraphIndex
 
2433
 
 
2434
    @property
 
2435
    def _serializer(self):
 
2436
        return xml5.serializer_v5
2246
2437
 
2247
2438
    def _get_matching_bzrdir(self):
2248
 
        return bzrdir.format_registry.make_bzrdir('development0')
 
2439
        return bzrdir.format_registry.make_bzrdir('development2')
2249
2440
 
2250
2441
    def _ignore_setting_bzrdir(self, format):
2251
2442
        pass
2254
2445
 
2255
2446
    def get_format_string(self):
2256
2447
        """See RepositoryFormat.get_format_string()."""
2257
 
        return "Bazaar development format 0 (needs bzr.dev from before 1.3)\n"
 
2448
        return "Bazaar development format 2 (needs bzr.dev from before 1.8)\n"
2258
2449
 
2259
2450
    def get_format_description(self):
2260
2451
        """See RepositoryFormat.get_format_description()."""
2261
2452
        return ("Development repository format, currently the same as "
2262
 
            "pack-0.92\n")
 
2453
            "1.6.1 with B+Trees.\n")
2263
2454
 
2264
2455
    def check_conversion_target(self, target_format):
2265
2456
        pass
2266
2457
 
2267
2458
 
2268
 
class RepositoryFormatPackDevelopment0Subtree(RepositoryFormatPack):
 
2459
class RepositoryFormatPackDevelopment2Subtree(RepositoryFormatPack):
2269
2460
    """A subtrees development repository.
2270
2461
 
2271
 
    This format should be retained until the second release after bzr 1.0.
 
2462
    This format should be retained until the second release after bzr 1.7.
2272
2463
 
2273
 
    No changes to the disk behaviour from pack-0.92-subtree.
 
2464
    1.6.1-subtree[as it might have been] with B+Tree indices.
2274
2465
    """
2275
2466
 
2276
2467
    repository_class = KnitPackRepository
2277
2468
    _commit_builder_class = PackRootCommitBuilder
2278
2469
    rich_root_data = True
2279
2470
    supports_tree_reference = True
2280
 
    _serializer = xml7.serializer_v7
2281
 
 
2282
 
    def _get_matching_bzrdir(self):
2283
 
        return bzrdir.format_registry.make_bzrdir(
2284
 
            'development0-subtree')
2285
 
 
2286
 
    def _ignore_setting_bzrdir(self, format):
2287
 
        pass
2288
 
 
2289
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2290
 
 
2291
 
    def check_conversion_target(self, target_format):
2292
 
        if not target_format.rich_root_data:
2293
 
            raise errors.BadConversionTarget(
2294
 
                'Does not support rich root data.', target_format)
2295
 
        if not getattr(target_format, 'supports_tree_reference', False):
2296
 
            raise errors.BadConversionTarget(
2297
 
                'Does not support nested trees', target_format)
2298
 
            
2299
 
    def get_format_string(self):
2300
 
        """See RepositoryFormat.get_format_string()."""
2301
 
        return ("Bazaar development format 0 with subtree support "
2302
 
            "(needs bzr.dev from before 1.3)\n")
2303
 
 
2304
 
    def get_format_description(self):
2305
 
        """See RepositoryFormat.get_format_description()."""
2306
 
        return ("Development repository format, currently the same as "
2307
 
            "pack-0.92-subtree\n")
2308
 
 
2309
 
 
2310
 
class RepositoryFormatPackDevelopment1(RepositoryFormatPackDevelopment0):
2311
 
    """A no-subtrees development repository.
2312
 
 
2313
 
    This format should be retained until the second release after bzr 1.5.
2314
 
 
2315
 
    Supports external lookups, which results in non-truncated ghosts after
2316
 
    reconcile compared to pack-0.92 formats.
2317
 
    """
2318
 
 
2319
 
    supports_external_lookups = True
2320
 
 
2321
 
    def _get_matching_bzrdir(self):
2322
 
        return bzrdir.format_registry.make_bzrdir('development1')
2323
 
 
2324
 
    def _ignore_setting_bzrdir(self, format):
2325
 
        pass
2326
 
 
2327
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2328
 
 
2329
 
    def get_format_string(self):
2330
 
        """See RepositoryFormat.get_format_string()."""
2331
 
        return "Bazaar development format 1 (needs bzr.dev from before 1.6)\n"
2332
 
 
2333
 
    def get_format_description(self):
2334
 
        """See RepositoryFormat.get_format_description()."""
2335
 
        return ("Development repository format, currently the same as "
2336
 
            "pack-0.92 with external reference support.\n")
2337
 
 
2338
 
    def check_conversion_target(self, target_format):
2339
 
        pass
2340
 
 
2341
 
 
2342
 
class RepositoryFormatPackDevelopment1Subtree(RepositoryFormatPackDevelopment0Subtree):
2343
 
    """A subtrees development repository.
2344
 
 
2345
 
    This format should be retained until the second release after bzr 1.5.
2346
 
 
2347
 
    Supports external lookups, which results in non-truncated ghosts after
2348
 
    reconcile compared to pack-0.92 formats.
2349
 
    """
2350
 
 
2351
 
    supports_external_lookups = True
2352
 
 
2353
 
    def _get_matching_bzrdir(self):
2354
 
        return bzrdir.format_registry.make_bzrdir(
2355
 
            'development1-subtree')
2356
 
 
2357
 
    def _ignore_setting_bzrdir(self, format):
2358
 
        pass
2359
 
 
2360
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
2361
 
 
2362
 
    def check_conversion_target(self, target_format):
2363
 
        if not target_format.rich_root_data:
2364
 
            raise errors.BadConversionTarget(
2365
 
                'Does not support rich root data.', target_format)
2366
 
        if not getattr(target_format, 'supports_tree_reference', False):
2367
 
            raise errors.BadConversionTarget(
2368
 
                'Does not support nested trees', target_format)
2369
 
            
2370
 
    def get_format_string(self):
2371
 
        """See RepositoryFormat.get_format_string()."""
2372
 
        return ("Bazaar development format 1 with subtree support "
2373
 
            "(needs bzr.dev from before 1.6)\n")
2374
 
 
2375
 
    def get_format_description(self):
2376
 
        """See RepositoryFormat.get_format_description()."""
2377
 
        return ("Development repository format, currently the same as "
2378
 
            "pack-0.92-subtree with external reference support.\n")
 
2471
    supports_external_lookups = True
 
2472
    # What index classes to use
 
2473
    index_builder_class = BTreeBuilder
 
2474
    index_class = BTreeGraphIndex
 
2475
 
 
2476
    @property
 
2477
    def _serializer(self):
 
2478
        return xml7.serializer_v7
 
2479
 
 
2480
    def _get_matching_bzrdir(self):
 
2481
        return bzrdir.format_registry.make_bzrdir(
 
2482
            'development2-subtree')
 
2483
 
 
2484
    def _ignore_setting_bzrdir(self, format):
 
2485
        pass
 
2486
 
 
2487
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
2488
 
 
2489
    def check_conversion_target(self, target_format):
 
2490
        if not target_format.rich_root_data:
 
2491
            raise errors.BadConversionTarget(
 
2492
                'Does not support rich root data.', target_format)
 
2493
        if not getattr(target_format, 'supports_tree_reference', False):
 
2494
            raise errors.BadConversionTarget(
 
2495
                'Does not support nested trees', target_format)
 
2496
            
 
2497
    def get_format_string(self):
 
2498
        """See RepositoryFormat.get_format_string()."""
 
2499
        return ("Bazaar development format 2 with subtree support "
 
2500
            "(needs bzr.dev from before 1.8)\n")
 
2501
 
 
2502
    def get_format_description(self):
 
2503
        """See RepositoryFormat.get_format_description()."""
 
2504
        return ("Development repository format, currently the same as "
 
2505
            "1.6.1-subtree with B+Tree indices.\n")