~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repofmt/groupcompress_repo.py

  • Committer: Martin
  • Date: 2010-05-16 15:18:43 UTC
  • mfrom: (5235 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5239.
  • Revision ID: gzlist@googlemail.com-20100516151843-lu53u7caehm3ie3i
Merge bzr.dev to resolve conflicts in NEWS and _chk_map_pyx

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
                if new_inv.root_id is None:
 
866
                    raise errors.RootMissing()
 
867
                inv_lines = new_inv.to_lines()
 
868
                return self._inventory_add_lines(new_revision_id, parents,
 
869
                    inv_lines, check_content=False), new_inv
 
870
            else:
 
871
                basis_tree = self.revision_tree(basis_revision_id)
 
872
                basis_tree.lock_read()
 
873
                basis_inv = basis_tree.inventory
 
874
        try:
 
875
            result = basis_inv.create_by_apply_delta(delta, new_revision_id,
 
876
                propagate_caches=propagate_caches)
 
877
            inv_lines = result.to_lines()
 
878
            return self._inventory_add_lines(new_revision_id, parents,
 
879
                inv_lines, check_content=False), result
 
880
        finally:
 
881
            if basis_tree is not None:
 
882
                basis_tree.unlock()
 
883
 
 
884
    def _deserialise_inventory(self, revision_id, bytes):
 
885
        return inventory.CHKInventory.deserialise(self.chk_bytes, bytes,
 
886
            (revision_id,))
 
887
 
 
888
    def _iter_inventories(self, revision_ids, ordering):
 
889
        """Iterate over many inventory objects."""
 
890
        if ordering is None:
 
891
            ordering = 'unordered'
 
892
        keys = [(revision_id,) for revision_id in revision_ids]
 
893
        stream = self.inventories.get_record_stream(keys, ordering, True)
 
894
        texts = {}
 
895
        for record in stream:
 
896
            if record.storage_kind != 'absent':
 
897
                texts[record.key] = record.get_bytes_as('fulltext')
 
898
            else:
 
899
                raise errors.NoSuchRevision(self, record.key)
 
900
        for key in keys:
 
901
            yield inventory.CHKInventory.deserialise(self.chk_bytes, texts[key], key)
 
902
 
 
903
    def _iter_inventory_xmls(self, revision_ids, ordering):
 
904
        # Without a native 'xml' inventory, this method doesn't make sense.
 
905
        # However older working trees, and older bundles want it - so we supply
 
906
        # it allowing _get_inventory_xml to work. Bundles currently use the
 
907
        # serializer directly; this also isn't ideal, but there isn't an xml
 
908
        # iteration interface offered at all for repositories. We could make
 
909
        # _iter_inventory_xmls be part of the contract, even if kept private.
 
910
        inv_to_str = self._serializer.write_inventory_to_string
 
911
        for inv in self.iter_inventories(revision_ids, ordering=ordering):
 
912
            yield inv_to_str(inv), inv.revision_id
 
913
 
 
914
    def _find_present_inventory_keys(self, revision_keys):
 
915
        parent_map = self.inventories.get_parent_map(revision_keys)
 
916
        present_inventory_keys = set(k for k in parent_map)
 
917
        return present_inventory_keys
 
918
 
 
919
    def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
 
920
        """Find the file ids and versions affected by revisions.
 
921
 
 
922
        :param revisions: an iterable containing revision ids.
 
923
        :param _inv_weave: The inventory weave from this repository or None.
 
924
            If None, the inventory weave will be opened automatically.
 
925
        :return: a dictionary mapping altered file-ids to an iterable of
 
926
            revision_ids. Each altered file-ids has the exact revision_ids that
 
927
            altered it listed explicitly.
 
928
        """
 
929
        rich_root = self.supports_rich_root()
 
930
        bytes_to_info = inventory.CHKInventory._bytes_to_utf8name_key
 
931
        file_id_revisions = {}
 
932
        pb = ui.ui_factory.nested_progress_bar()
 
933
        try:
 
934
            revision_keys = [(r,) for r in revision_ids]
 
935
            parent_keys = self._find_parent_keys_of_revisions(revision_keys)
 
936
            # TODO: instead of using _find_present_inventory_keys, change the
 
937
            #       code paths to allow missing inventories to be tolerated.
 
938
            #       However, we only want to tolerate missing parent
 
939
            #       inventories, not missing inventories for revision_ids
 
940
            present_parent_inv_keys = self._find_present_inventory_keys(
 
941
                                        parent_keys)
 
942
            present_parent_inv_ids = set(
 
943
                [k[-1] for k in present_parent_inv_keys])
 
944
            inventories_to_read = set(revision_ids)
 
945
            inventories_to_read.update(present_parent_inv_ids)
 
946
            root_key_info = _build_interesting_key_sets(
 
947
                self, inventories_to_read, present_parent_inv_ids)
 
948
            interesting_root_keys = root_key_info.interesting_root_keys
 
949
            uninteresting_root_keys = root_key_info.uninteresting_root_keys
 
950
            chk_bytes = self.chk_bytes
 
951
            for record, items in chk_map.iter_interesting_nodes(chk_bytes,
 
952
                        interesting_root_keys, uninteresting_root_keys,
 
953
                        pb=pb):
 
954
                for name, bytes in items:
 
955
                    (name_utf8, file_id, revision_id) = bytes_to_info(bytes)
 
956
                    # TODO: consider interning file_id, revision_id here, or
 
957
                    #       pushing that intern() into bytes_to_info()
 
958
                    # TODO: rich_root should always be True here, for all
 
959
                    #       repositories that support chk_bytes
 
960
                    if not rich_root and name_utf8 == '':
 
961
                        continue
 
962
                    try:
 
963
                        file_id_revisions[file_id].add(revision_id)
 
964
                    except KeyError:
 
965
                        file_id_revisions[file_id] = set([revision_id])
 
966
        finally:
 
967
            pb.finished()
 
968
        return file_id_revisions
 
969
 
 
970
    def find_text_key_references(self):
 
971
        """Find the text key references within the repository.
 
972
 
 
973
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
 
974
            to whether they were referred to by the inventory of the
 
975
            revision_id that they contain. The inventory texts from all present
 
976
            revision ids are assessed to generate this report.
 
977
        """
 
978
        # XXX: Slow version but correct: rewrite as a series of delta
 
979
        # examinations/direct tree traversal. Note that that will require care
 
980
        # as a common node is reachable both from the inventory that added it,
 
981
        # and others afterwards.
 
982
        revision_keys = self.revisions.keys()
 
983
        result = {}
 
984
        rich_roots = self.supports_rich_root()
 
985
        pb = ui.ui_factory.nested_progress_bar()
 
986
        try:
 
987
            all_revs = self.all_revision_ids()
 
988
            total = len(all_revs)
 
989
            for pos, inv in enumerate(self.iter_inventories(all_revs)):
 
990
                pb.update("Finding text references", pos, total)
 
991
                for _, entry in inv.iter_entries():
 
992
                    if not rich_roots and entry.file_id == inv.root_id:
 
993
                        continue
 
994
                    key = (entry.file_id, entry.revision)
 
995
                    result.setdefault(key, False)
 
996
                    if entry.revision == inv.revision_id:
 
997
                        result[key] = True
 
998
            return result
 
999
        finally:
 
1000
            pb.finished()
721
1001
 
722
1002
    def _reconcile_pack(self, collection, packs, extension, revs, pb):
723
 
        # assert revs is None
724
1003
        packer = GCCHKReconcilePacker(collection, packs, extension)
725
1004
        return packer.pack(pb)
726
1005
 
727
 
 
728
 
class RepositoryFormatPackGCCHK16(RepositoryFormatPackDevelopment5Hash16):
 
1006
    def _get_source(self, to_format):
 
1007
        """Return a source for streaming from this repository."""
 
1008
        if self._format._serializer == to_format._serializer:
 
1009
            # We must be exactly the same format, otherwise stuff like the chk
 
1010
            # page layout might be different.
 
1011
            # Actually, this test is just slightly looser than exact so that
 
1012
            # CHK2 <-> 2a transfers will work.
 
1013
            return GroupCHKStreamSource(self, to_format)
 
1014
        return super(CHKInventoryRepository, self)._get_source(to_format)
 
1015
 
 
1016
 
 
1017
class GroupCHKStreamSource(KnitPackStreamSource):
 
1018
    """Used when both the source and target repo are GroupCHK repos."""
 
1019
 
 
1020
    def __init__(self, from_repository, to_format):
 
1021
        """Create a StreamSource streaming from from_repository."""
 
1022
        super(GroupCHKStreamSource, self).__init__(from_repository, to_format)
 
1023
        self._revision_keys = None
 
1024
        self._text_keys = None
 
1025
        self._text_fetch_order = 'groupcompress'
 
1026
        self._chk_id_roots = None
 
1027
        self._chk_p_id_roots = None
 
1028
 
 
1029
    def _get_inventory_stream(self, inventory_keys, allow_absent=False):
 
1030
        """Get a stream of inventory texts.
 
1031
 
 
1032
        When this function returns, self._chk_id_roots and self._chk_p_id_roots
 
1033
        should be populated.
 
1034
        """
 
1035
        self._chk_id_roots = []
 
1036
        self._chk_p_id_roots = []
 
1037
        def _filtered_inv_stream():
 
1038
            id_roots_set = set()
 
1039
            p_id_roots_set = set()
 
1040
            source_vf = self.from_repository.inventories
 
1041
            stream = source_vf.get_record_stream(inventory_keys,
 
1042
                                                 'groupcompress', True)
 
1043
            for record in stream:
 
1044
                if record.storage_kind == 'absent':
 
1045
                    if allow_absent:
 
1046
                        continue
 
1047
                    else:
 
1048
                        raise errors.NoSuchRevision(self, record.key)
 
1049
                bytes = record.get_bytes_as('fulltext')
 
1050
                chk_inv = inventory.CHKInventory.deserialise(None, bytes,
 
1051
                                                             record.key)
 
1052
                key = chk_inv.id_to_entry.key()
 
1053
                if key not in id_roots_set:
 
1054
                    self._chk_id_roots.append(key)
 
1055
                    id_roots_set.add(key)
 
1056
                p_id_map = chk_inv.parent_id_basename_to_file_id
 
1057
                if p_id_map is None:
 
1058
                    raise AssertionError('Parent id -> file_id map not set')
 
1059
                key = p_id_map.key()
 
1060
                if key not in p_id_roots_set:
 
1061
                    p_id_roots_set.add(key)
 
1062
                    self._chk_p_id_roots.append(key)
 
1063
                yield record
 
1064
            # We have finished processing all of the inventory records, we
 
1065
            # don't need these sets anymore
 
1066
            id_roots_set.clear()
 
1067
            p_id_roots_set.clear()
 
1068
        return ('inventories', _filtered_inv_stream())
 
1069
 
 
1070
    def _get_filtered_chk_streams(self, excluded_revision_keys):
 
1071
        self._text_keys = set()
 
1072
        excluded_revision_keys.discard(_mod_revision.NULL_REVISION)
 
1073
        if not excluded_revision_keys:
 
1074
            uninteresting_root_keys = set()
 
1075
            uninteresting_pid_root_keys = set()
 
1076
        else:
 
1077
            # filter out any excluded revisions whose inventories are not
 
1078
            # actually present
 
1079
            # TODO: Update Repository.iter_inventories() to add
 
1080
            #       ignore_missing=True
 
1081
            present_keys = self.from_repository._find_present_inventory_keys(
 
1082
                            excluded_revision_keys)
 
1083
            present_ids = [k[-1] for k in present_keys]
 
1084
            uninteresting_root_keys = set()
 
1085
            uninteresting_pid_root_keys = set()
 
1086
            for inv in self.from_repository.iter_inventories(present_ids):
 
1087
                uninteresting_root_keys.add(inv.id_to_entry.key())
 
1088
                uninteresting_pid_root_keys.add(
 
1089
                    inv.parent_id_basename_to_file_id.key())
 
1090
        chk_bytes = self.from_repository.chk_bytes
 
1091
        def _filter_id_to_entry():
 
1092
            interesting_nodes = chk_map.iter_interesting_nodes(chk_bytes,
 
1093
                        self._chk_id_roots, uninteresting_root_keys)
 
1094
            for record in _filter_text_keys(interesting_nodes, self._text_keys,
 
1095
                    chk_map._bytes_to_text_key):
 
1096
                if record is not None:
 
1097
                    yield record
 
1098
            # Consumed
 
1099
            self._chk_id_roots = None
 
1100
        yield 'chk_bytes', _filter_id_to_entry()
 
1101
        def _get_parent_id_basename_to_file_id_pages():
 
1102
            for record, items in chk_map.iter_interesting_nodes(chk_bytes,
 
1103
                        self._chk_p_id_roots, uninteresting_pid_root_keys):
 
1104
                if record is not None:
 
1105
                    yield record
 
1106
            # Consumed
 
1107
            self._chk_p_id_roots = None
 
1108
        yield 'chk_bytes', _get_parent_id_basename_to_file_id_pages()
 
1109
 
 
1110
    def get_stream(self, search):
 
1111
        revision_ids = search.get_keys()
 
1112
        for stream_info in self._fetch_revision_texts(revision_ids):
 
1113
            yield stream_info
 
1114
        self._revision_keys = [(rev_id,) for rev_id in revision_ids]
 
1115
        self.from_repository.revisions.clear_cache()
 
1116
        self.from_repository.signatures.clear_cache()
 
1117
        yield self._get_inventory_stream(self._revision_keys)
 
1118
        self.from_repository.inventories.clear_cache()
 
1119
        # TODO: The keys to exclude might be part of the search recipe
 
1120
        # For now, exclude all parents that are at the edge of ancestry, for
 
1121
        # which we have inventories
 
1122
        from_repo = self.from_repository
 
1123
        parent_keys = from_repo._find_parent_keys_of_revisions(
 
1124
                        self._revision_keys)
 
1125
        for stream_info in self._get_filtered_chk_streams(parent_keys):
 
1126
            yield stream_info
 
1127
        self.from_repository.chk_bytes.clear_cache()
 
1128
        yield self._get_text_stream()
 
1129
        self.from_repository.texts.clear_cache()
 
1130
 
 
1131
    def get_stream_for_missing_keys(self, missing_keys):
 
1132
        # missing keys can only occur when we are byte copying and not
 
1133
        # translating (because translation means we don't send
 
1134
        # unreconstructable deltas ever).
 
1135
        missing_inventory_keys = set()
 
1136
        for key in missing_keys:
 
1137
            if key[0] != 'inventories':
 
1138
                raise AssertionError('The only missing keys we should'
 
1139
                    ' be filling in are inventory keys, not %s'
 
1140
                    % (key[0],))
 
1141
            missing_inventory_keys.add(key[1:])
 
1142
        if self._chk_id_roots or self._chk_p_id_roots:
 
1143
            raise AssertionError('Cannot call get_stream_for_missing_keys'
 
1144
                ' until all of get_stream() has been consumed.')
 
1145
        # Yield the inventory stream, so we can find the chk stream
 
1146
        # Some of the missing_keys will be missing because they are ghosts.
 
1147
        # As such, we can ignore them. The Sink is required to verify there are
 
1148
        # no unavailable texts when the ghost inventories are not filled in.
 
1149
        yield self._get_inventory_stream(missing_inventory_keys,
 
1150
                                         allow_absent=True)
 
1151
        # We use the empty set for excluded_revision_keys, to make it clear
 
1152
        # that we want to transmit all referenced chk pages.
 
1153
        for stream_info in self._get_filtered_chk_streams(set()):
 
1154
            yield stream_info
 
1155
 
 
1156
 
 
1157
class _InterestingKeyInfo(object):
 
1158
    def __init__(self):
 
1159
        self.interesting_root_keys = set()
 
1160
        self.interesting_pid_root_keys = set()
 
1161
        self.uninteresting_root_keys = set()
 
1162
        self.uninteresting_pid_root_keys = set()
 
1163
 
 
1164
    def all_interesting(self):
 
1165
        return self.interesting_root_keys.union(self.interesting_pid_root_keys)
 
1166
 
 
1167
    def all_uninteresting(self):
 
1168
        return self.uninteresting_root_keys.union(
 
1169
            self.uninteresting_pid_root_keys)
 
1170
 
 
1171
    def all_keys(self):
 
1172
        return self.all_interesting().union(self.all_uninteresting())
 
1173
 
 
1174
 
 
1175
def _build_interesting_key_sets(repo, inventory_ids, parent_only_inv_ids):
 
1176
    result = _InterestingKeyInfo()
 
1177
    for inv in repo.iter_inventories(inventory_ids, 'unordered'):
 
1178
        root_key = inv.id_to_entry.key()
 
1179
        pid_root_key = inv.parent_id_basename_to_file_id.key()
 
1180
        if inv.revision_id in parent_only_inv_ids:
 
1181
            result.uninteresting_root_keys.add(root_key)
 
1182
            result.uninteresting_pid_root_keys.add(pid_root_key)
 
1183
        else:
 
1184
            result.interesting_root_keys.add(root_key)
 
1185
            result.interesting_pid_root_keys.add(pid_root_key)
 
1186
    return result
 
1187
 
 
1188
 
 
1189
def _filter_text_keys(interesting_nodes_iterable, text_keys, bytes_to_text_key):
 
1190
    """Iterate the result of iter_interesting_nodes, yielding the records
 
1191
    and adding to text_keys.
 
1192
    """
 
1193
    text_keys_update = text_keys.update
 
1194
    for record, items in interesting_nodes_iterable:
 
1195
        text_keys_update([bytes_to_text_key(b) for n,b in items])
 
1196
        yield record
 
1197
 
 
1198
 
 
1199
 
 
1200
 
 
1201
class RepositoryFormatCHK1(RepositoryFormatPack):
729
1202
    """A hashed CHK+group compress pack repository."""
730
1203
 
731
 
    repository_class = GCCHKPackRepository
732
 
    _commit_builder_class = PackRootCommitBuilder
733
 
    rich_root_data = True
 
1204
    repository_class = CHKInventoryRepository
734
1205
    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
1206
    supports_chks = True
814
1207
    # For right now, setting this to True gives us InterModel1And2 rather
815
1208
    # than InterDifferingSerializer
816
1209
    _commit_builder_class = PackRootCommitBuilder
817
1210
    rich_root_data = True
818
1211
    _serializer = chk_serializer.chk_serializer_255_bigpage
 
1212
    _commit_inv_deltas = True
 
1213
    # What index classes to use
 
1214
    index_builder_class = BTreeBuilder
 
1215
    index_class = BTreeGraphIndex
819
1216
    # Note: We cannot unpack a delta that references a text we haven't
820
1217
    # seen yet. There are 2 options, work in fulltexts, or require
821
1218
    # topological sorting. Using fulltexts is more optimal for local
823
1220
    # multiple in-a-row (and sharing strings). Topological is better
824
1221
    # for remote, because we access less data.
825
1222
    _fetch_order = 'unordered'
826
 
    _fetch_uses_deltas = False
 
1223
    _fetch_uses_deltas = False # essentially ignored by the groupcompress code.
 
1224
    fast_deltas = True
 
1225
    pack_compresses = True
827
1226
 
828
1227
    def _get_matching_bzrdir(self):
829
 
        return bzrdir.format_registry.make_bzrdir('gc-chk255-big')
 
1228
        return bzrdir.format_registry.make_bzrdir('development6-rich-root')
830
1229
 
831
1230
    def _ignore_setting_bzrdir(self, format):
832
1231
        pass
835
1234
 
836
1235
    def get_format_string(self):
837
1236
        """See RepositoryFormat.get_format_string()."""
838
 
        return ('Bazaar development format - hash255chk+gc rich-root bigpage'
 
1237
        return ('Bazaar development format - group compression and chk inventory'
839
1238
                ' (needs bzr.dev from 1.14)\n')
840
1239
 
841
1240
    def get_format_description(self):
842
1241
        """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)
 
1242
        return ("Development repository format - rich roots, group compression"
 
1243
            " and chk inventories")
 
1244
 
 
1245
 
 
1246
class RepositoryFormatCHK2(RepositoryFormatCHK1):
 
1247
    """A CHK repository that uses the bencode revision serializer."""
 
1248
 
 
1249
    _serializer = chk_serializer.chk_bencode_serializer
 
1250
 
 
1251
    def _get_matching_bzrdir(self):
 
1252
        return bzrdir.format_registry.make_bzrdir('development7-rich-root')
 
1253
 
 
1254
    def _ignore_setting_bzrdir(self, format):
 
1255
        pass
 
1256
 
 
1257
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
1258
 
 
1259
    def get_format_string(self):
 
1260
        """See RepositoryFormat.get_format_string()."""
 
1261
        return ('Bazaar development format - chk repository with bencode '
 
1262
                'revision serialization (needs bzr.dev from 1.16)\n')
 
1263
 
 
1264
 
 
1265
class RepositoryFormat2a(RepositoryFormatCHK2):
 
1266
    """A CHK repository that uses the bencode revision serializer.
 
1267
 
 
1268
    This is the same as RepositoryFormatCHK2 but with a public name.
 
1269
    """
 
1270
 
 
1271
    _serializer = chk_serializer.chk_bencode_serializer
 
1272
 
 
1273
    def _get_matching_bzrdir(self):
 
1274
        return bzrdir.format_registry.make_bzrdir('2a')
 
1275
 
 
1276
    def _ignore_setting_bzrdir(self, format):
 
1277
        pass
 
1278
 
 
1279
    _matchingbzrdir = property(_get_matching_bzrdir, _ignore_setting_bzrdir)
 
1280
 
 
1281
    def get_format_string(self):
 
1282
        return ('Bazaar repository format 2a (needs bzr 1.16 or later)\n')
 
1283
 
 
1284
    def get_format_description(self):
 
1285
        """See RepositoryFormat.get_format_description()."""
 
1286
        return ("Repository format 2a - rich roots, group compression"
 
1287
            " and chk inventories")