~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/groupcompress_repo.py

  • Committer: John Arbash Meinel
  • Date: 2010-05-11 10:45:26 UTC
  • mto: This revision was merged to the branch mainline in revision 5225.
  • Revision ID: john@arbash-meinel.com-20100511104526-zxnstcxta22hzw2n
Implement a compiled extension for parsing the text key out of a CHKInventory value.

Related to bug #562666. This seems to shave 5-10% out of the time spent doing a complete
branch of bzr.dev/launchpad/etc.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2008, 2009, 2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
"""Repostory formats using B+Tree indices and groupcompress compression."""
 
17
"""Repository formats using CHK inventories and groupcompress compression."""
18
18
 
19
19
import time
20
20
 
29
29
    knit,
30
30
    osutils,
31
31
    pack,
32
 
    repository,
33
32
    revision as _mod_revision,
34
33
    trace,
35
34
    ui,
36
35
    )
37
 
from bzrlib.index import GraphIndex, GraphIndexBuilder
 
36
from bzrlib.btree_index import (
 
37
    BTreeGraphIndex,
 
38
    BTreeBuilder,
 
39
    )
38
40
from bzrlib.groupcompress import (
39
41
    _GCGraphIndex,
40
42
    GroupCompressVersionedFiles,
43
45
    Pack,
44
46
    NewPack,
45
47
    KnitPackRepository,
 
48
    KnitPackStreamSource,
46
49
    PackRootCommitBuilder,
47
50
    RepositoryPackCollection,
48
 
    RepositoryFormatKnitPack6,
 
51
    RepositoryFormatPack,
 
52
    ResumedPack,
49
53
    Packer,
50
 
    CHKInventoryRepository,
51
 
    RepositoryFormatPackDevelopment5Hash16,
52
 
    RepositoryFormatPackDevelopment5Hash255,
53
54
    )
54
 
 
 
55
from bzrlib.static_tuple import StaticTuple
55
56
 
56
57
 
57
58
class GCPack(NewPack):
86
87
            # have a regular 2-list index giving parents and compression
87
88
            # source.
88
89
            index_builder_class(reference_lists=1),
89
 
            # Texts: compression and per file graph, for all fileids - so two
90
 
            # reference lists and two elements in the key tuple.
 
90
            # Texts: per file graph, for all fileids - so one reference list
 
91
            # and two elements in the key tuple.
91
92
            index_builder_class(reference_lists=1, key_elements=2),
92
93
            # Signatures: Just blobs to store, no compression, no parents
93
94
            # listing.
153
154
        self._writer.begin()
154
155
        # what state is the pack in? (open, finished, aborted)
155
156
        self._state = 'open'
 
157
        # no name until we finish writing the content
 
158
        self.name = None
156
159
 
157
160
    def _check_references(self):
158
161
        """Make sure our external references are present.
162
165
        have deltas based on a fallback repository.
163
166
        (See <https://bugs.launchpad.net/bzr/+bug/288751>)
164
167
        """
165
 
        # Groupcompress packs don't have any external references
 
168
        # Groupcompress packs don't have any external references, arguably CHK
 
169
        # pages have external references, but we cannot 'cheaply' determine
 
170
        # them without actually walking all of the chk pages.
 
171
 
 
172
 
 
173
class ResumedGCPack(ResumedPack):
 
174
 
 
175
    def _check_references(self):
 
176
        """Make sure our external compression parents are present."""
 
177
        # See GCPack._check_references for why this is empty
 
178
 
 
179
    def _get_external_refs(self, index):
 
180
        # GC repositories don't have compression parents external to a given
 
181
        # pack file
 
182
        return set()
166
183
 
167
184
 
168
185
class GCCHKPacker(Packer):
175
192
                                          reload_func=reload_func)
176
193
        self._pack_collection = pack_collection
177
194
        # ATM, We only support this for GCCHK repositories
178
 
        assert pack_collection.chk_index is not None
 
195
        if pack_collection.chk_index is None:
 
196
            raise AssertionError('pack_collection.chk_index should not be None')
179
197
        self._gather_text_refs = False
180
198
        self._chk_id_roots = []
181
199
        self._chk_p_id_roots = []
200
218
            p_id_roots_set = set()
201
219
            stream = source_vf.get_record_stream(keys, 'groupcompress', True)
202
220
            for idx, record in enumerate(stream):
 
221
                # Inventories should always be with revisions; assume success.
203
222
                bytes = record.get_bytes_as('fulltext')
204
223
                chk_inv = inventory.CHKInventory.deserialise(None, bytes,
205
224
                                                             record.key)
210
229
                    self._chk_id_roots.append(key)
211
230
                    id_roots_set.add(key)
212
231
                p_id_map = chk_inv.parent_id_basename_to_file_id
213
 
                assert p_id_map is not None
 
232
                if p_id_map is None:
 
233
                    raise AssertionError('Parent id -> file_id map not set')
214
234
                key = p_id_map.key()
215
235
                if key not in p_id_roots_set:
216
236
                    p_id_roots_set.add(key)
242
262
        remaining_keys = set(keys)
243
263
        counter = [0]
244
264
        if self._gather_text_refs:
245
 
            bytes_to_info = inventory.CHKInventory._bytes_to_utf8name_key
246
265
            self._text_refs = set()
247
266
        def _get_referenced_stream(root_keys, parse_leaf_nodes=False):
248
267
            cur_keys = root_keys
269
288
                    # Store is None, because we know we have a LeafNode, and we
270
289
                    # just want its entries
271
290
                    for file_id, bytes in node.iteritems(None):
272
 
                        name_utf8, file_id, revision_id = bytes_to_info(bytes)
273
 
                        self._text_refs.add((file_id, revision))
 
291
                        self._text_refs.add(chk_map._bytes_to_text_key(bytes))
274
292
                def next_stream():
275
293
                    stream = source_vf.get_record_stream(cur_keys,
276
294
                                                         'as-requested', True)
277
295
                    for record in stream:
 
296
                        if record.storage_kind == 'absent':
 
297
                            # An absent CHK record: we assume that the missing
 
298
                            # record is in a different pack - e.g. a page not
 
299
                            # altered by the commit we're packing.
 
300
                            continue
278
301
                        bytes = record.get_bytes_as('fulltext')
279
302
                        # We don't care about search_key_func for this code,
280
303
                        # because we only care about external references.
327
350
        """Build a VersionedFiles instance on top of this group of packs."""
328
351
        index_name = index_name + '_index'
329
352
        index_to_pack = {}
330
 
        access = knit._DirectPackAccess(index_to_pack)
 
353
        access = knit._DirectPackAccess(index_to_pack,
 
354
                                        reload_func=self._reload_func)
331
355
        if for_write:
332
356
            # Use new_pack
333
 
            assert self.new_pack is not None
 
357
            if self.new_pack is None:
 
358
                raise AssertionError('No new pack has been set')
334
359
            index = getattr(self.new_pack, index_name)
335
360
            index_to_pack[index] = self.new_pack.access_tuple()
336
361
            index.set_optimize(for_size=True)
386
411
 
387
412
    def _copy_inventory_texts(self):
388
413
        source_vf, target_vf = self._build_vfs('inventory', True, True)
389
 
        self._copy_stream(source_vf, target_vf, self.revision_keys,
 
414
        # It is not sufficient to just use self.revision_keys, as stacked
 
415
        # repositories can have more inventories than they have revisions.
 
416
        # One alternative would be to do something with
 
417
        # get_parent_map(self.revision_keys), but that shouldn't be any faster
 
418
        # than this.
 
419
        inventory_keys = source_vf.keys()
 
420
        missing_inventories = set(self.revision_keys).difference(inventory_keys)
 
421
        if missing_inventories:
 
422
            missing_inventories = sorted(missing_inventories)
 
423
            raise ValueError('We are missing inventories for revisions: %s'
 
424
                % (missing_inventories,))
 
425
        self._copy_stream(source_vf, target_vf, inventory_keys,
390
426
                          'inventories', self._get_filtered_inv_stream, 2)
391
427
 
392
428
    def _copy_chk_texts(self):
419
455
        #      is grabbing too many keys...
420
456
        text_keys = source_vf.keys()
421
457
        self._copy_stream(source_vf, target_vf, text_keys,
422
 
                          'text', self._get_progress_stream, 4)
 
458
                          'texts', self._get_progress_stream, 4)
423
459
 
424
460
    def _copy_signature_texts(self):
425
461
        source_vf, target_vf = self._build_vfs('signature', False, False)
442
478
        if not self._use_pack(self.new_pack):
443
479
            self.new_pack.abort()
444
480
            return None
 
481
        self.new_pack.finish_content()
 
482
        if len(self.packs) == 1:
 
483
            old_pack = self.packs[0]
 
484
            if old_pack.name == self.new_pack._hash.hexdigest():
 
485
                # The single old pack was already optimally packed.
 
486
                trace.mutter('single pack %s was already optimally packed',
 
487
                    old_pack.name)
 
488
                self.new_pack.abort()
 
489
                return None
445
490
        self.pb.update('finishing repack', 6, 7)
446
491
        self.new_pack.finish()
447
492
        self._pack_collection.allocate(self.new_pack)
536
581
class GCRepositoryPackCollection(RepositoryPackCollection):
537
582
 
538
583
    pack_factory = GCPack
539
 
 
540
 
    def _already_packed(self):
541
 
        """Is the collection already packed?"""
542
 
        # Always repack GC repositories for now
543
 
        return False
 
584
    resumed_pack_factory = ResumedGCPack
 
585
 
 
586
    def _check_new_inventories(self):
 
587
        """Detect missing inventories or chk root entries for the new revisions
 
588
        in this write group.
 
589
 
 
590
        :returns: list of strs, summarising any problems found.  If the list is
 
591
            empty no problems were found.
 
592
        """
 
593
        # Ensure that all revisions added in this write group have:
 
594
        #   - corresponding inventories,
 
595
        #   - chk root entries for those inventories,
 
596
        #   - and any present parent inventories have their chk root
 
597
        #     entries too.
 
598
        # And all this should be independent of any fallback repository.
 
599
        problems = []
 
600
        key_deps = self.repo.revisions._index._key_dependencies
 
601
        new_revisions_keys = key_deps.get_new_keys()
 
602
        no_fallback_inv_index = self.repo.inventories._index
 
603
        no_fallback_chk_bytes_index = self.repo.chk_bytes._index
 
604
        no_fallback_texts_index = self.repo.texts._index
 
605
        inv_parent_map = no_fallback_inv_index.get_parent_map(
 
606
            new_revisions_keys)
 
607
        # Are any inventories for corresponding to the new revisions missing?
 
608
        corresponding_invs = set(inv_parent_map)
 
609
        missing_corresponding = set(new_revisions_keys)
 
610
        missing_corresponding.difference_update(corresponding_invs)
 
611
        if missing_corresponding:
 
612
            problems.append("inventories missing for revisions %s" %
 
613
                (sorted(missing_corresponding),))
 
614
            return problems
 
615
        # Are any chk root entries missing for any inventories?  This includes
 
616
        # any present parent inventories, which may be used when calculating
 
617
        # deltas for streaming.
 
618
        all_inv_keys = set(corresponding_invs)
 
619
        for parent_inv_keys in inv_parent_map.itervalues():
 
620
            all_inv_keys.update(parent_inv_keys)
 
621
        # Filter out ghost parents.
 
622
        all_inv_keys.intersection_update(
 
623
            no_fallback_inv_index.get_parent_map(all_inv_keys))
 
624
        parent_invs_only_keys = all_inv_keys.symmetric_difference(
 
625
            corresponding_invs)
 
626
        all_missing = set()
 
627
        inv_ids = [key[-1] for key in all_inv_keys]
 
628
        parent_invs_only_ids = [key[-1] for key in parent_invs_only_keys]
 
629
        root_key_info = _build_interesting_key_sets(
 
630
            self.repo, inv_ids, parent_invs_only_ids)
 
631
        expected_chk_roots = root_key_info.all_keys()
 
632
        present_chk_roots = no_fallback_chk_bytes_index.get_parent_map(
 
633
            expected_chk_roots)
 
634
        missing_chk_roots = expected_chk_roots.difference(present_chk_roots)
 
635
        if missing_chk_roots:
 
636
            problems.append("missing referenced chk root keys: %s"
 
637
                % (sorted(missing_chk_roots),))
 
638
            # Don't bother checking any further.
 
639
            return problems
 
640
        # Find all interesting chk_bytes records, and make sure they are
 
641
        # present, as well as the text keys they reference.
 
642
        chk_bytes_no_fallbacks = self.repo.chk_bytes.without_fallbacks()
 
643
        chk_bytes_no_fallbacks._search_key_func = \
 
644
            self.repo.chk_bytes._search_key_func
 
645
        chk_diff = chk_map.iter_interesting_nodes(
 
646
            chk_bytes_no_fallbacks, root_key_info.interesting_root_keys,
 
647
            root_key_info.uninteresting_root_keys)
 
648
        text_keys = set()
 
649
        try:
 
650
            for record in _filter_text_keys(chk_diff, text_keys,
 
651
                                            chk_map._bytes_to_text_key):
 
652
                pass
 
653
        except errors.NoSuchRevision, e:
 
654
            # XXX: It would be nice if we could give a more precise error here.
 
655
            problems.append("missing chk node(s) for id_to_entry maps")
 
656
        chk_diff = chk_map.iter_interesting_nodes(
 
657
            chk_bytes_no_fallbacks, root_key_info.interesting_pid_root_keys,
 
658
            root_key_info.uninteresting_pid_root_keys)
 
659
        try:
 
660
            for interesting_rec, interesting_map in chk_diff:
 
661
                pass
 
662
        except errors.NoSuchRevision, e:
 
663
            problems.append(
 
664
                "missing chk node(s) for parent_id_basename_to_file_id maps")
 
665
        present_text_keys = no_fallback_texts_index.get_parent_map(text_keys)
 
666
        missing_text_keys = text_keys.difference(present_text_keys)
 
667
        if missing_text_keys:
 
668
            problems.append("missing text keys: %r"
 
669
                % (sorted(missing_text_keys),))
 
670
        return problems
544
671
 
545
672
    def _execute_pack_operations(self, pack_operations,
546
673
                                 _packer_class=GCCHKPacker,
560
687
            packer = GCCHKPacker(self, packs, '.autopack',
561
688
                                 reload_func=reload_func)
562
689
            try:
563
 
                packer.pack()
 
690
                result = packer.pack()
564
691
            except errors.RetryWithNewPacks:
565
692
                # An exception is propagating out of this context, make sure
566
693
                # this packer has cleaned up. Packer() doesn't set its new_pack
569
696
                if packer.new_pack is not None:
570
697
                    packer.new_pack.abort()
571
698
                raise
 
699
            if result is None:
 
700
                return
572
701
            for pack in packs:
573
702
                self._remove_pack_from_memory(pack)
574
703
        # record the newly available packs and stop advertising the old
575
704
        # packs
576
 
        self._save_pack_names(clear_obsolete_packs=True)
577
 
        # Move the old packs out of the way now they are no longer referenced.
578
 
        for revision_count, packs in pack_operations:
579
 
            self._obsolete_packs(packs)
580
 
 
581
 
 
582
 
# XXX: This format is scheduled for termination
583
 
#
584
 
# class GCPackRepository(KnitPackRepository):
585
 
#     """GC customisation of KnitPackRepository."""
586
 
#
587
 
#     def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
588
 
#         _serializer):
589
 
#         """Overridden to change pack collection class."""
590
 
#         KnitPackRepository.__init__(self, _format, a_bzrdir, control_files,
591
 
#             _commit_builder_class, _serializer)
592
 
#         # and now replace everything it did :)
593
 
#         index_transport = self._transport.clone('indices')
594
 
#         self._pack_collection = GCRepositoryPackCollection(self,
595
 
#             self._transport, index_transport,
596
 
#             self._transport.clone('upload'),
597
 
#             self._transport.clone('packs'),
598
 
#             _format.index_builder_class,
599
 
#             _format.index_class,
600
 
#             use_chk_index=self._format.supports_chks,
601
 
#             )
602
 
#         self.inventories = GroupCompressVersionedFiles(
603
 
#             _GCGraphIndex(self._pack_collection.inventory_index.combined_index,
604
 
#                 add_callback=self._pack_collection.inventory_index.add_callback,
605
 
#                 parents=True, is_locked=self.is_locked),
606
 
#             access=self._pack_collection.inventory_index.data_access)
607
 
#         self.revisions = GroupCompressVersionedFiles(
608
 
#             _GCGraphIndex(self._pack_collection.revision_index.combined_index,
609
 
#                 add_callback=self._pack_collection.revision_index.add_callback,
610
 
#                 parents=True, is_locked=self.is_locked),
611
 
#             access=self._pack_collection.revision_index.data_access,
612
 
#             delta=False)
613
 
#         self.signatures = GroupCompressVersionedFiles(
614
 
#             _GCGraphIndex(self._pack_collection.signature_index.combined_index,
615
 
#                 add_callback=self._pack_collection.signature_index.add_callback,
616
 
#                 parents=False, is_locked=self.is_locked),
617
 
#             access=self._pack_collection.signature_index.data_access,
618
 
#             delta=False)
619
 
#         self.texts = GroupCompressVersionedFiles(
620
 
#             _GCGraphIndex(self._pack_collection.text_index.combined_index,
621
 
#                 add_callback=self._pack_collection.text_index.add_callback,
622
 
#                 parents=True, is_locked=self.is_locked),
623
 
#             access=self._pack_collection.text_index.data_access)
624
 
#         if _format.supports_chks:
625
 
#             # No graph, no compression:- references from chks are between
626
 
#             # different objects not temporal versions of the same; and without
627
 
#             # some sort of temporal structure knit compression will just fail.
628
 
#             self.chk_bytes = GroupCompressVersionedFiles(
629
 
#                 _GCGraphIndex(self._pack_collection.chk_index.combined_index,
630
 
#                     add_callback=self._pack_collection.chk_index.add_callback,
631
 
#                     parents=False, is_locked=self.is_locked),
632
 
#                 access=self._pack_collection.chk_index.data_access)
633
 
#         else:
634
 
#             self.chk_bytes = None
635
 
#         # True when the repository object is 'write locked' (as opposed to the
636
 
#         # physical lock only taken out around changes to the pack-names list.)
637
 
#         # Another way to represent this would be a decorator around the control
638
 
#         # files object that presents logical locks as physical ones - if this
639
 
#         # gets ugly consider that alternative design. RBC 20071011
640
 
#         self._write_lock_count = 0
641
 
#         self._transaction = None
642
 
#         # for tests
643
 
#         self._reconcile_does_inventory_gc = True
644
 
#         self._reconcile_fixes_text_parents = True
645
 
#         self._reconcile_backsup_inventory = False
646
 
#
647
 
#     def suspend_write_group(self):
648
 
#         raise errors.UnsuspendableWriteGroup(self)
649
 
#
650
 
#     def _resume_write_group(self, tokens):
651
 
#         raise errors.UnsuspendableWriteGroup(self)
652
 
#
653
 
#     def _reconcile_pack(self, collection, packs, extension, revs, pb):
654
 
#         bork
655
 
#         return packer.pack(pb)
656
 
 
657
 
 
658
 
class GCCHKPackRepository(CHKInventoryRepository):
659
 
    """GC customisation of CHKInventoryRepository."""
 
705
        to_be_obsoleted = []
 
706
        for _, packs in pack_operations:
 
707
            to_be_obsoleted.extend(packs)
 
708
        result = self._save_pack_names(clear_obsolete_packs=True,
 
709
                                       obsolete_packs=to_be_obsoleted)
 
710
        return result
 
711
 
 
712
 
 
713
class CHKInventoryRepository(KnitPackRepository):
 
714
    """subclass of KnitPackRepository that uses CHK based inventories."""
660
715
 
661
716
    def __init__(self, _format, a_bzrdir, control_files, _commit_builder_class,
662
717
        _serializer):
676
731
        self.inventories = GroupCompressVersionedFiles(
677
732
            _GCGraphIndex(self._pack_collection.inventory_index.combined_index,
678
733
                add_callback=self._pack_collection.inventory_index.add_callback,
679
 
                parents=True, is_locked=self.is_locked),
 
734
                parents=True, is_locked=self.is_locked,
 
735
                inconsistency_fatal=False),
680
736
            access=self._pack_collection.inventory_index.data_access)
681
737
        self.revisions = GroupCompressVersionedFiles(
682
738
            _GCGraphIndex(self._pack_collection.revision_index.combined_index,
683
739
                add_callback=self._pack_collection.revision_index.add_callback,
684
 
                parents=True, is_locked=self.is_locked),
 
740
                parents=True, is_locked=self.is_locked,
 
741
                track_external_parent_refs=True, track_new_keys=True),
685
742
            access=self._pack_collection.revision_index.data_access,
686
743
            delta=False)
687
744
        self.signatures = GroupCompressVersionedFiles(
688
745
            _GCGraphIndex(self._pack_collection.signature_index.combined_index,
689
746
                add_callback=self._pack_collection.signature_index.add_callback,
690
 
                parents=False, is_locked=self.is_locked),
 
747
                parents=False, is_locked=self.is_locked,
 
748
                inconsistency_fatal=False),
691
749
            access=self._pack_collection.signature_index.data_access,
692
750
            delta=False)
693
751
        self.texts = GroupCompressVersionedFiles(
694
752
            _GCGraphIndex(self._pack_collection.text_index.combined_index,
695
753
                add_callback=self._pack_collection.text_index.add_callback,
696
 
                parents=True, is_locked=self.is_locked),
 
754
                parents=True, is_locked=self.is_locked,
 
755
                inconsistency_fatal=False),
697
756
            access=self._pack_collection.text_index.data_access)
698
757
        # No parents, individual CHK pages don't have specific ancestry
699
758
        self.chk_bytes = GroupCompressVersionedFiles(
700
759
            _GCGraphIndex(self._pack_collection.chk_index.combined_index,
701
760
                add_callback=self._pack_collection.chk_index.add_callback,
702
 
                parents=False, is_locked=self.is_locked),
 
761
                parents=False, is_locked=self.is_locked,
 
762
                inconsistency_fatal=False),
703
763
            access=self._pack_collection.chk_index.data_access)
 
764
        search_key_name = self._format._serializer.search_key_name
 
765
        search_key_func = chk_map.search_key_registry.get(search_key_name)
 
766
        self.chk_bytes._search_key_func = search_key_func
704
767
        # True when the repository object is 'write locked' (as opposed to the
705
768
        # physical lock only taken out around changes to the pack-names list.)
706
769
        # Another way to represent this would be a decorator around the control
713
776
        self._reconcile_fixes_text_parents = True
714
777
        self._reconcile_backsup_inventory = False
715
778
 
716
 
    def suspend_write_group(self):
717
 
        raise errors.UnsuspendableWriteGroup(self)
718
 
 
719
 
    def _resume_write_group(self, tokens):
720
 
        raise errors.UnsuspendableWriteGroup(self)
 
779
    def _add_inventory_checked(self, revision_id, inv, parents):
 
780
        """Add inv to the repository after checking the inputs.
 
781
 
 
782
        This function can be overridden to allow different inventory styles.
 
783
 
 
784
        :seealso: add_inventory, for the contract.
 
785
        """
 
786
        # make inventory
 
787
        serializer = self._format._serializer
 
788
        result = inventory.CHKInventory.from_inventory(self.chk_bytes, inv,
 
789
            maximum_size=serializer.maximum_size,
 
790
            search_key_name=serializer.search_key_name)
 
791
        inv_lines = result.to_lines()
 
792
        return self._inventory_add_lines(revision_id, parents,
 
793
            inv_lines, check_content=False)
 
794
 
 
795
    def _create_inv_from_null(self, delta, revision_id):
 
796
        """This will mutate new_inv directly.
 
797
 
 
798
        This is a simplified form of create_by_apply_delta which knows that all
 
799
        the old values must be None, so everything is a create.
 
800
        """
 
801
        serializer = self._format._serializer
 
802
        new_inv = inventory.CHKInventory(serializer.search_key_name)
 
803
        new_inv.revision_id = revision_id
 
804
        entry_to_bytes = new_inv._entry_to_bytes
 
805
        id_to_entry_dict = {}
 
806
        parent_id_basename_dict = {}
 
807
        for old_path, new_path, file_id, entry in delta:
 
808
            if old_path is not None:
 
809
                raise ValueError('Invalid delta, somebody tried to delete %r'
 
810
                                 ' from the NULL_REVISION'
 
811
                                 % ((old_path, file_id),))
 
812
            if new_path is None:
 
813
                raise ValueError('Invalid delta, delta from NULL_REVISION has'
 
814
                                 ' no new_path %r' % (file_id,))
 
815
            if new_path == '':
 
816
                new_inv.root_id = file_id
 
817
                parent_id_basename_key = StaticTuple('', '').intern()
 
818
            else:
 
819
                utf8_entry_name = entry.name.encode('utf-8')
 
820
                parent_id_basename_key = StaticTuple(entry.parent_id,
 
821
                                                     utf8_entry_name).intern()
 
822
            new_value = entry_to_bytes(entry)
 
823
            # Populate Caches?
 
824
            # new_inv._path_to_fileid_cache[new_path] = file_id
 
825
            key = StaticTuple(file_id).intern()
 
826
            id_to_entry_dict[key] = new_value
 
827
            parent_id_basename_dict[parent_id_basename_key] = file_id
 
828
 
 
829
        new_inv._populate_from_dicts(self.chk_bytes, id_to_entry_dict,
 
830
            parent_id_basename_dict, maximum_size=serializer.maximum_size)
 
831
        return new_inv
 
832
 
 
833
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
 
834
                               parents, basis_inv=None, propagate_caches=False):
 
835
        """Add a new inventory expressed as a delta against another revision.
 
836
 
 
837
        :param basis_revision_id: The inventory id the delta was created
 
838
            against.
 
839
        :param delta: The inventory delta (see Inventory.apply_delta for
 
840
            details).
 
841
        :param new_revision_id: The revision id that the inventory is being
 
842
            added for.
 
843
        :param parents: The revision ids of the parents that revision_id is
 
844
            known to have and are in the repository already. These are supplied
 
845
            for repositories that depend on the inventory graph for revision
 
846
            graph access, as well as for those that pun ancestry with delta
 
847
            compression.
 
848
        :param basis_inv: The basis inventory if it is already known,
 
849
            otherwise None.
 
850
        :param propagate_caches: If True, the caches for this inventory are
 
851
          copied to and updated for the result if possible.
 
852
 
 
853
        :returns: (validator, new_inv)
 
854
            The validator(which is a sha1 digest, though what is sha'd is
 
855
            repository format specific) of the serialized inventory, and the
 
856
            resulting inventory.
 
857
        """
 
858
        if not self.is_in_write_group():
 
859
            raise AssertionError("%r not in write group" % (self,))
 
860
        _mod_revision.check_not_reserved_id(new_revision_id)
 
861
        basis_tree = None
 
862
        if basis_inv is None:
 
863
            if basis_revision_id == _mod_revision.NULL_REVISION:
 
864
                new_inv = self._create_inv_from_null(delta, new_revision_id)
 
865
                inv_lines = new_inv.to_lines()
 
866
                return self._inventory_add_lines(new_revision_id, parents,
 
867
                    inv_lines, check_content=False), new_inv
 
868
            else:
 
869
                basis_tree = self.revision_tree(basis_revision_id)
 
870
                basis_tree.lock_read()
 
871
                basis_inv = basis_tree.inventory
 
872
        try:
 
873
            result = basis_inv.create_by_apply_delta(delta, new_revision_id,
 
874
                propagate_caches=propagate_caches)
 
875
            inv_lines = result.to_lines()
 
876
            return self._inventory_add_lines(new_revision_id, parents,
 
877
                inv_lines, check_content=False), result
 
878
        finally:
 
879
            if basis_tree is not None:
 
880
                basis_tree.unlock()
 
881
 
 
882
    def _deserialise_inventory(self, revision_id, bytes):
 
883
        return inventory.CHKInventory.deserialise(self.chk_bytes, bytes,
 
884
            (revision_id,))
 
885
 
 
886
    def _iter_inventories(self, revision_ids, ordering):
 
887
        """Iterate over many inventory objects."""
 
888
        if ordering is None:
 
889
            ordering = 'unordered'
 
890
        keys = [(revision_id,) for revision_id in revision_ids]
 
891
        stream = self.inventories.get_record_stream(keys, ordering, True)
 
892
        texts = {}
 
893
        for record in stream:
 
894
            if record.storage_kind != 'absent':
 
895
                texts[record.key] = record.get_bytes_as('fulltext')
 
896
            else:
 
897
                raise errors.NoSuchRevision(self, record.key)
 
898
        for key in keys:
 
899
            yield inventory.CHKInventory.deserialise(self.chk_bytes, texts[key], key)
 
900
 
 
901
    def _iter_inventory_xmls(self, revision_ids, ordering):
 
902
        # Without a native 'xml' inventory, this method doesn't make sense.
 
903
        # However older working trees, and older bundles want it - so we supply
 
904
        # it allowing _get_inventory_xml to work. Bundles currently use the
 
905
        # serializer directly; this also isn't ideal, but there isn't an xml
 
906
        # iteration interface offered at all for repositories. We could make
 
907
        # _iter_inventory_xmls be part of the contract, even if kept private.
 
908
        inv_to_str = self._serializer.write_inventory_to_string
 
909
        for inv in self.iter_inventories(revision_ids, ordering=ordering):
 
910
            yield inv_to_str(inv), inv.revision_id
 
911
 
 
912
    def _find_present_inventory_keys(self, revision_keys):
 
913
        parent_map = self.inventories.get_parent_map(revision_keys)
 
914
        present_inventory_keys = set(k for k in parent_map)
 
915
        return present_inventory_keys
 
916
 
 
917
    def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
 
918
        """Find the file ids and versions affected by revisions.
 
919
 
 
920
        :param revisions: an iterable containing revision ids.
 
921
        :param _inv_weave: The inventory weave from this repository or None.
 
922
            If None, the inventory weave will be opened automatically.
 
923
        :return: a dictionary mapping altered file-ids to an iterable of
 
924
            revision_ids. Each altered file-ids has the exact revision_ids that
 
925
            altered it listed explicitly.
 
926
        """
 
927
        rich_root = self.supports_rich_root()
 
928
        bytes_to_info = inventory.CHKInventory._bytes_to_utf8name_key
 
929
        file_id_revisions = {}
 
930
        pb = ui.ui_factory.nested_progress_bar()
 
931
        try:
 
932
            revision_keys = [(r,) for r in revision_ids]
 
933
            parent_keys = self._find_parent_keys_of_revisions(revision_keys)
 
934
            # TODO: instead of using _find_present_inventory_keys, change the
 
935
            #       code paths to allow missing inventories to be tolerated.
 
936
            #       However, we only want to tolerate missing parent
 
937
            #       inventories, not missing inventories for revision_ids
 
938
            present_parent_inv_keys = self._find_present_inventory_keys(
 
939
                                        parent_keys)
 
940
            present_parent_inv_ids = set(
 
941
                [k[-1] for k in present_parent_inv_keys])
 
942
            inventories_to_read = set(revision_ids)
 
943
            inventories_to_read.update(present_parent_inv_ids)
 
944
            root_key_info = _build_interesting_key_sets(
 
945
                self, inventories_to_read, present_parent_inv_ids)
 
946
            interesting_root_keys = root_key_info.interesting_root_keys
 
947
            uninteresting_root_keys = root_key_info.uninteresting_root_keys
 
948
            chk_bytes = self.chk_bytes
 
949
            for record, items in chk_map.iter_interesting_nodes(chk_bytes,
 
950
                        interesting_root_keys, uninteresting_root_keys,
 
951
                        pb=pb):
 
952
                for name, bytes in items:
 
953
                    (name_utf8, file_id, revision_id) = bytes_to_info(bytes)
 
954
                    # TODO: consider interning file_id, revision_id here, or
 
955
                    #       pushing that intern() into bytes_to_info()
 
956
                    # TODO: rich_root should always be True here, for all
 
957
                    #       repositories that support chk_bytes
 
958
                    if not rich_root and name_utf8 == '':
 
959
                        continue
 
960
                    try:
 
961
                        file_id_revisions[file_id].add(revision_id)
 
962
                    except KeyError:
 
963
                        file_id_revisions[file_id] = set([revision_id])
 
964
        finally:
 
965
            pb.finished()
 
966
        return file_id_revisions
 
967
 
 
968
    def find_text_key_references(self):
 
969
        """Find the text key references within the repository.
 
970
 
 
971
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
 
972
            to whether they were referred to by the inventory of the
 
973
            revision_id that they contain. The inventory texts from all present
 
974
            revision ids are assessed to generate this report.
 
975
        """
 
976
        # XXX: Slow version but correct: rewrite as a series of delta
 
977
        # examinations/direct tree traversal. Note that that will require care
 
978
        # as a common node is reachable both from the inventory that added it,
 
979
        # and others afterwards.
 
980
        revision_keys = self.revisions.keys()
 
981
        result = {}
 
982
        rich_roots = self.supports_rich_root()
 
983
        pb = ui.ui_factory.nested_progress_bar()
 
984
        try:
 
985
            all_revs = self.all_revision_ids()
 
986
            total = len(all_revs)
 
987
            for pos, inv in enumerate(self.iter_inventories(all_revs)):
 
988
                pb.update("Finding text references", pos, total)
 
989
                for _, entry in inv.iter_entries():
 
990
                    if not rich_roots and entry.file_id == inv.root_id:
 
991
                        continue
 
992
                    key = (entry.file_id, entry.revision)
 
993
                    result.setdefault(key, False)
 
994
                    if entry.revision == inv.revision_id:
 
995
                        result[key] = True
 
996
            return result
 
997
        finally:
 
998
            pb.finished()
721
999
 
722
1000
    def _reconcile_pack(self, collection, packs, extension, revs, pb):
723
 
        # assert revs is None
724
1001
        packer = GCCHKReconcilePacker(collection, packs, extension)
725
1002
        return packer.pack(pb)
726
1003
 
727
 
 
728
 
class RepositoryFormatPackGCCHK16(RepositoryFormatPackDevelopment5Hash16):
 
1004
    def _get_source(self, to_format):
 
1005
        """Return a source for streaming from this repository."""
 
1006
        if self._format._serializer == to_format._serializer:
 
1007
            # We must be exactly the same format, otherwise stuff like the chk
 
1008
            # page layout might be different.
 
1009
            # Actually, this test is just slightly looser than exact so that
 
1010
            # CHK2 <-> 2a transfers will work.
 
1011
            return GroupCHKStreamSource(self, to_format)
 
1012
        return super(CHKInventoryRepository, self)._get_source(to_format)
 
1013
 
 
1014
 
 
1015
class GroupCHKStreamSource(KnitPackStreamSource):
 
1016
    """Used when both the source and target repo are GroupCHK repos."""
 
1017
 
 
1018
    def __init__(self, from_repository, to_format):
 
1019
        """Create a StreamSource streaming from from_repository."""
 
1020
        super(GroupCHKStreamSource, self).__init__(from_repository, to_format)
 
1021
        self._revision_keys = None
 
1022
        self._text_keys = None
 
1023
        self._text_fetch_order = 'groupcompress'
 
1024
        self._chk_id_roots = None
 
1025
        self._chk_p_id_roots = None
 
1026
 
 
1027
    def _get_inventory_stream(self, inventory_keys, allow_absent=False):
 
1028
        """Get a stream of inventory texts.
 
1029
 
 
1030
        When this function returns, self._chk_id_roots and self._chk_p_id_roots
 
1031
        should be populated.
 
1032
        """
 
1033
        self._chk_id_roots = []
 
1034
        self._chk_p_id_roots = []
 
1035
        def _filtered_inv_stream():
 
1036
            id_roots_set = set()
 
1037
            p_id_roots_set = set()
 
1038
            source_vf = self.from_repository.inventories
 
1039
            stream = source_vf.get_record_stream(inventory_keys,
 
1040
                                                 'groupcompress', True)
 
1041
            for record in stream:
 
1042
                if record.storage_kind == 'absent':
 
1043
                    if allow_absent:
 
1044
                        continue
 
1045
                    else:
 
1046
                        raise errors.NoSuchRevision(self, record.key)
 
1047
                bytes = record.get_bytes_as('fulltext')
 
1048
                chk_inv = inventory.CHKInventory.deserialise(None, bytes,
 
1049
                                                             record.key)
 
1050
                key = chk_inv.id_to_entry.key()
 
1051
                if key not in id_roots_set:
 
1052
                    self._chk_id_roots.append(key)
 
1053
                    id_roots_set.add(key)
 
1054
                p_id_map = chk_inv.parent_id_basename_to_file_id
 
1055
                if p_id_map is None:
 
1056
                    raise AssertionError('Parent id -> file_id map not set')
 
1057
                key = p_id_map.key()
 
1058
                if key not in p_id_roots_set:
 
1059
                    p_id_roots_set.add(key)
 
1060
                    self._chk_p_id_roots.append(key)
 
1061
                yield record
 
1062
            # We have finished processing all of the inventory records, we
 
1063
            # don't need these sets anymore
 
1064
            id_roots_set.clear()
 
1065
            p_id_roots_set.clear()
 
1066
        return ('inventories', _filtered_inv_stream())
 
1067
 
 
1068
    def _get_filtered_chk_streams(self, excluded_revision_keys):
 
1069
        self._text_keys = set()
 
1070
        excluded_revision_keys.discard(_mod_revision.NULL_REVISION)
 
1071
        if not excluded_revision_keys:
 
1072
            uninteresting_root_keys = set()
 
1073
            uninteresting_pid_root_keys = set()
 
1074
        else:
 
1075
            # filter out any excluded revisions whose inventories are not
 
1076
            # actually present
 
1077
            # TODO: Update Repository.iter_inventories() to add
 
1078
            #       ignore_missing=True
 
1079
            present_keys = self.from_repository._find_present_inventory_keys(
 
1080
                            excluded_revision_keys)
 
1081
            present_ids = [k[-1] for k in present_keys]
 
1082
            uninteresting_root_keys = set()
 
1083
            uninteresting_pid_root_keys = set()
 
1084
            for inv in self.from_repository.iter_inventories(present_ids):
 
1085
                uninteresting_root_keys.add(inv.id_to_entry.key())
 
1086
                uninteresting_pid_root_keys.add(
 
1087
                    inv.parent_id_basename_to_file_id.key())
 
1088
        chk_bytes = self.from_repository.chk_bytes
 
1089
        def _filter_id_to_entry():
 
1090
            interesting_nodes = chk_map.iter_interesting_nodes(chk_bytes,
 
1091
                        self._chk_id_roots, uninteresting_root_keys)
 
1092
            for record in _filter_text_keys(interesting_nodes, self._text_keys,
 
1093
                    chk_map._bytes_to_text_key):
 
1094
                if record is not None:
 
1095
                    yield record
 
1096
            # Consumed
 
1097
            self._chk_id_roots = None
 
1098
        yield 'chk_bytes', _filter_id_to_entry()
 
1099
        def _get_parent_id_basename_to_file_id_pages():
 
1100
            for record, items in chk_map.iter_interesting_nodes(chk_bytes,
 
1101
                        self._chk_p_id_roots, uninteresting_pid_root_keys):
 
1102
                if record is not None:
 
1103
                    yield record
 
1104
            # Consumed
 
1105
            self._chk_p_id_roots = None
 
1106
        yield 'chk_bytes', _get_parent_id_basename_to_file_id_pages()
 
1107
 
 
1108
    def get_stream(self, search):
 
1109
        revision_ids = search.get_keys()
 
1110
        for stream_info in self._fetch_revision_texts(revision_ids):
 
1111
            yield stream_info
 
1112
        self._revision_keys = [(rev_id,) for rev_id in revision_ids]
 
1113
        self.from_repository.revisions.clear_cache()
 
1114
        self.from_repository.signatures.clear_cache()
 
1115
        yield self._get_inventory_stream(self._revision_keys)
 
1116
        self.from_repository.inventories.clear_cache()
 
1117
        # TODO: The keys to exclude might be part of the search recipe
 
1118
        # For now, exclude all parents that are at the edge of ancestry, for
 
1119
        # which we have inventories
 
1120
        from_repo = self.from_repository
 
1121
        parent_keys = from_repo._find_parent_keys_of_revisions(
 
1122
                        self._revision_keys)
 
1123
        for stream_info in self._get_filtered_chk_streams(parent_keys):
 
1124
            yield stream_info
 
1125
        self.from_repository.chk_bytes.clear_cache()
 
1126
        yield self._get_text_stream()
 
1127
        self.from_repository.texts.clear_cache()
 
1128
 
 
1129
    def get_stream_for_missing_keys(self, missing_keys):
 
1130
        # missing keys can only occur when we are byte copying and not
 
1131
        # translating (because translation means we don't send
 
1132
        # unreconstructable deltas ever).
 
1133
        missing_inventory_keys = set()
 
1134
        for key in missing_keys:
 
1135
            if key[0] != 'inventories':
 
1136
                raise AssertionError('The only missing keys we should'
 
1137
                    ' be filling in are inventory keys, not %s'
 
1138
                    % (key[0],))
 
1139
            missing_inventory_keys.add(key[1:])
 
1140
        if self._chk_id_roots or self._chk_p_id_roots:
 
1141
            raise AssertionError('Cannot call get_stream_for_missing_keys'
 
1142
                ' until all of get_stream() has been consumed.')
 
1143
        # Yield the inventory stream, so we can find the chk stream
 
1144
        # Some of the missing_keys will be missing because they are ghosts.
 
1145
        # As such, we can ignore them. The Sink is required to verify there are
 
1146
        # no unavailable texts when the ghost inventories are not filled in.
 
1147
        yield self._get_inventory_stream(missing_inventory_keys,
 
1148
                                         allow_absent=True)
 
1149
        # We use the empty set for excluded_revision_keys, to make it clear
 
1150
        # that we want to transmit all referenced chk pages.
 
1151
        for stream_info in self._get_filtered_chk_streams(set()):
 
1152
            yield stream_info
 
1153
 
 
1154
 
 
1155
class _InterestingKeyInfo(object):
 
1156
    def __init__(self):
 
1157
        self.interesting_root_keys = set()
 
1158
        self.interesting_pid_root_keys = set()
 
1159
        self.uninteresting_root_keys = set()
 
1160
        self.uninteresting_pid_root_keys = set()
 
1161
 
 
1162
    def all_interesting(self):
 
1163
        return self.interesting_root_keys.union(self.interesting_pid_root_keys)
 
1164
 
 
1165
    def all_uninteresting(self):
 
1166
        return self.uninteresting_root_keys.union(
 
1167
            self.uninteresting_pid_root_keys)
 
1168
 
 
1169
    def all_keys(self):
 
1170
        return self.all_interesting().union(self.all_uninteresting())
 
1171
 
 
1172
 
 
1173
def _build_interesting_key_sets(repo, inventory_ids, parent_only_inv_ids):
 
1174
    result = _InterestingKeyInfo()
 
1175
    for inv in repo.iter_inventories(inventory_ids, 'unordered'):
 
1176
        root_key = inv.id_to_entry.key()
 
1177
        pid_root_key = inv.parent_id_basename_to_file_id.key()
 
1178
        if inv.revision_id in parent_only_inv_ids:
 
1179
            result.uninteresting_root_keys.add(root_key)
 
1180
            result.uninteresting_pid_root_keys.add(pid_root_key)
 
1181
        else:
 
1182
            result.interesting_root_keys.add(root_key)
 
1183
            result.interesting_pid_root_keys.add(pid_root_key)
 
1184
    return result
 
1185
 
 
1186
 
 
1187
def _filter_text_keys(interesting_nodes_iterable, text_keys, bytes_to_text_key):
 
1188
    """Iterate the result of iter_interesting_nodes, yielding the records
 
1189
    and adding to text_keys.
 
1190
    """
 
1191
    text_keys_update = text_keys.update
 
1192
    for record, items in interesting_nodes_iterable:
 
1193
        text_keys_update([bytes_to_text_key(b) for n,b in items])
 
1194
        yield record
 
1195
 
 
1196
 
 
1197
 
 
1198
 
 
1199
class RepositoryFormatCHK1(RepositoryFormatPack):
729
1200
    """A hashed CHK+group compress pack repository."""
730
1201
 
731
 
    repository_class = GCCHKPackRepository
732
 
    _commit_builder_class = PackRootCommitBuilder
733
 
    rich_root_data = True
 
1202
    repository_class = CHKInventoryRepository
734
1203
    supports_external_lookups = True
735
 
    supports_tree_reference = True
736
 
    supports_chks = True
737
 
    # Note: We cannot unpack a delta that references a text we haven't
738
 
    # seen yet. There are 2 options, work in fulltexts, or require
739
 
    # topological sorting. Using fulltexts is more optimal for local
740
 
    # operations, because the source can be smart about extracting
741
 
    # multiple in-a-row (and sharing strings). Topological is better
742
 
    # for remote, because we access less data.
743
 
    _fetch_order = 'unordered'
744
 
    _fetch_uses_deltas = False
745
 
 
746
 
    def _get_matching_bzrdir(self):
747
 
        return bzrdir.format_registry.make_bzrdir('gc-chk16')
748
 
 
749
 
    def _ignore_setting_bzrdir(self, format):
750
 
        pass
751
 
 
752
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
753
 
 
754
 
    def get_format_string(self):
755
 
        """See RepositoryFormat.get_format_string()."""
756
 
        return ('Bazaar development format - hash16chk+gc rich-root'
757
 
                ' (needs bzr.dev from 1.14)\n')
758
 
 
759
 
    def get_format_description(self):
760
 
        """See RepositoryFormat.get_format_description()."""
761
 
        return ("Development repository format - hash16chk+groupcompress")
762
 
 
763
 
    def check_conversion_target(self, target_format):
764
 
        if not target_format.rich_root_data:
765
 
            raise errors.BadConversionTarget(
766
 
                'Does not support rich root data.', target_format)
767
 
        if not getattr(target_format, 'supports_tree_reference', False):
768
 
            raise errors.BadConversionTarget(
769
 
                'Does not support nested trees', target_format)
770
 
 
771
 
 
772
 
class RepositoryFormatPackGCCHK255(RepositoryFormatPackDevelopment5Hash255):
773
 
    """A hashed CHK+group compress pack repository."""
774
 
 
775
 
    repository_class = GCCHKPackRepository
776
 
    supports_chks = True
777
 
    # Setting this to True causes us to use InterModel1And2, so for now set
778
 
    # it to False which uses InterDifferingSerializer. When IM1&2 is
779
 
    # removed (as it is in bzr.dev) we can set this back to True.
780
 
    _commit_builder_class = PackRootCommitBuilder
781
 
    rich_root_data = True
782
 
 
783
 
    def _get_matching_bzrdir(self):
784
 
        return bzrdir.format_registry.make_bzrdir('gc-chk255')
785
 
 
786
 
    def _ignore_setting_bzrdir(self, format):
787
 
        pass
788
 
 
789
 
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
790
 
 
791
 
    def get_format_string(self):
792
 
        """See RepositoryFormat.get_format_string()."""
793
 
        return ('Bazaar development format - hash255chk+gc rich-root'
794
 
                ' (needs bzr.dev from 1.14)\n')
795
 
 
796
 
    def get_format_description(self):
797
 
        """See RepositoryFormat.get_format_description()."""
798
 
        return ("Development repository format - hash255chk+groupcompress")
799
 
 
800
 
    def check_conversion_target(self, target_format):
801
 
        if not target_format.rich_root_data:
802
 
            raise errors.BadConversionTarget(
803
 
                'Does not support rich root data.', target_format)
804
 
        if not getattr(target_format, 'supports_tree_reference', False):
805
 
            raise errors.BadConversionTarget(
806
 
                'Does not support nested trees', target_format)
807
 
 
808
 
 
809
 
class RepositoryFormatPackGCCHK255Big(RepositoryFormatPackGCCHK255):
810
 
    """A hashed CHK+group compress pack repository."""
811
 
 
812
 
    repository_class = GCCHKPackRepository
813
1204
    supports_chks = True
814
1205
    # For right now, setting this to True gives us InterModel1And2 rather
815
1206
    # than InterDifferingSerializer
816
1207
    _commit_builder_class = PackRootCommitBuilder
817
1208
    rich_root_data = True
818
1209
    _serializer = chk_serializer.chk_serializer_255_bigpage
 
1210
    _commit_inv_deltas = True
 
1211
    # What index classes to use
 
1212
    index_builder_class = BTreeBuilder
 
1213
    index_class = BTreeGraphIndex
819
1214
    # Note: We cannot unpack a delta that references a text we haven't
820
1215
    # seen yet. There are 2 options, work in fulltexts, or require
821
1216
    # topological sorting. Using fulltexts is more optimal for local
823
1218
    # multiple in-a-row (and sharing strings). Topological is better
824
1219
    # for remote, because we access less data.
825
1220
    _fetch_order = 'unordered'
826
 
    _fetch_uses_deltas = False
 
1221
    _fetch_uses_deltas = False # essentially ignored by the groupcompress code.
 
1222
    fast_deltas = True
 
1223
    pack_compresses = True
827
1224
 
828
1225
    def _get_matching_bzrdir(self):
829
 
        return bzrdir.format_registry.make_bzrdir('gc-chk255-big')
 
1226
        return bzrdir.format_registry.make_bzrdir('development6-rich-root')
830
1227
 
831
1228
    def _ignore_setting_bzrdir(self, format):
832
1229
        pass
835
1232
 
836
1233
    def get_format_string(self):
837
1234
        """See RepositoryFormat.get_format_string()."""
838
 
        return ('Bazaar development format - hash255chk+gc rich-root bigpage'
 
1235
        return ('Bazaar development format - group compression and chk inventory'
839
1236
                ' (needs bzr.dev from 1.14)\n')
840
1237
 
841
1238
    def get_format_description(self):
842
1239
        """See RepositoryFormat.get_format_description()."""
843
 
        return ("Development repository format - hash255chk+groupcompress + bigpage")
844
 
 
845
 
    def check_conversion_target(self, target_format):
846
 
        if not target_format.rich_root_data:
847
 
            raise errors.BadConversionTarget(
848
 
                'Does not support rich root data.', target_format)
849
 
        if not getattr(target_format, 'supports_tree_reference', False):
850
 
            raise errors.BadConversionTarget(
851
 
                'Does not support nested trees', target_format)
 
1240
        return ("Development repository format - rich roots, group compression"
 
1241
            " and chk inventories")
 
1242
 
 
1243
 
 
1244
class RepositoryFormatCHK2(RepositoryFormatCHK1):
 
1245
    """A CHK repository that uses the bencode revision serializer."""
 
1246
 
 
1247
    _serializer = chk_serializer.chk_bencode_serializer
 
1248
 
 
1249
    def _get_matching_bzrdir(self):
 
1250
        return bzrdir.format_registry.make_bzrdir('development7-rich-root')
 
1251
 
 
1252
    def _ignore_setting_bzrdir(self, format):
 
1253
        pass
 
1254
 
 
1255
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
1256
 
 
1257
    def get_format_string(self):
 
1258
        """See RepositoryFormat.get_format_string()."""
 
1259
        return ('Bazaar development format - chk repository with bencode '
 
1260
                'revision serialization (needs bzr.dev from 1.16)\n')
 
1261
 
 
1262
 
 
1263
class RepositoryFormat2a(RepositoryFormatCHK2):
 
1264
    """A CHK repository that uses the bencode revision serializer.
 
1265
 
 
1266
    This is the same as RepositoryFormatCHK2 but with a public name.
 
1267
    """
 
1268
 
 
1269
    _serializer = chk_serializer.chk_bencode_serializer
 
1270
 
 
1271
    def _get_matching_bzrdir(self):
 
1272
        return bzrdir.format_registry.make_bzrdir('2a')
 
1273
 
 
1274
    def _ignore_setting_bzrdir(self, format):
 
1275
        pass
 
1276
 
 
1277
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
1278
 
 
1279
    def get_format_string(self):
 
1280
        return ('Bazaar repository format 2a (needs bzr 1.16 or later)\n')
 
1281
 
 
1282
    def get_format_description(self):
 
1283
        """See RepositoryFormat.get_format_description()."""
 
1284
        return ("Repository format 2a - rich roots, group compression"
 
1285
            " and chk inventories")