~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Robert Collins
  • Author(s): Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil
  • Date: 2009-04-07 12:32:42 UTC
  • mto: This revision was merged to the branch mainline in revision 4265.
  • Revision ID: robertc@robertcollins.net-20090407123242-x5htsk0p5k4zsetu
Add --development6-rich-root, disabling the legacy and unneeded development2 format, and activating the tests for CHK features disabled pending this format. (Robert Collins, John Arbash Meinel, Ian Clatworthy, Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
from bzrlib import (
24
24
    bzrdir,
25
25
    check,
 
26
    chk_map,
26
27
    debug,
27
28
    errors,
28
29
    fifo_cache,
29
30
    generate_ids,
30
31
    gpg,
31
32
    graph,
 
33
    inventory,
32
34
    lazy_regex,
33
35
    lockable_files,
34
36
    lockdir,
197
199
 
198
200
    def finish_inventory(self):
199
201
        """Tell the builder that the inventory is finished.
200
 
        
 
202
 
201
203
        :return: The inventory id in the repository, which can be used with
202
204
            repository.get_inventory.
203
205
        """
866
868
    which views a particular line of development through that history.
867
869
 
868
870
    The Repository builds on top of some byte storage facilies (the revisions,
869
 
    signatures, inventories and texts attributes) and a Transport, which
870
 
    respectively provide byte storage and a means to access the (possibly
 
871
    signatures, inventories, texts and chk_bytes attributes) and a Transport,
 
872
    which respectively provide byte storage and a means to access the (possibly
871
873
    remote) disk.
872
874
 
873
875
    The byte storage facilities are addressed via tuples, which we refer to
874
876
    as 'keys' throughout the code base. Revision_keys, inventory_keys and
875
877
    signature_keys are all 1-tuples: (revision_id,). text_keys are two-tuples:
876
 
    (file_id, revision_id). We use this interface because it allows low
877
 
    friction with the underlying code that implements disk indices, network
878
 
    encoding and other parts of bzrlib.
 
878
    (file_id, revision_id). chk_bytes uses CHK keys - a 1-tuple with a single
 
879
    byte string made up of a hash identifier and a hash value.
 
880
    We use this interface because it allows low friction with the underlying
 
881
    code that implements disk indices, network encoding and other parts of
 
882
    bzrlib.
879
883
 
880
884
    :ivar revisions: A bzrlib.versionedfile.VersionedFiles instance containing
881
885
        the serialised revisions for the repository. This can be used to obtain
900
904
        The result of trying to insert data into the repository via this store
901
905
        is undefined: it should be considered read-only except for implementors
902
906
        of repositories.
 
907
    :ivar chk_bytes: A bzrlib.versionedfile.VersionedFiles instance containing
 
908
        any data the repository chooses to store or have indexed by its hash.
 
909
        The result of trying to insert data into the repository via this store
 
910
        is undefined: it should be considered read-only except for implementors
 
911
        of repositories.
903
912
    :ivar _transport: Transport for file access to repository, typically
904
913
        pointing to .bzr/repository.
905
914
    """
926
935
        """
927
936
        if self._write_group is not self.get_transaction():
928
937
            # has an unlock or relock occured ?
929
 
            raise errors.BzrError('mismatched lock context and write group.')
 
938
            raise errors.BzrError(
 
939
                'mismatched lock context and write group. %r, %r' %
 
940
                (self._write_group, self.get_transaction()))
930
941
        try:
931
942
            self._abort_write_group()
932
943
        except Exception, exc:
964
975
        self.inventories.add_fallback_versioned_files(repository.inventories)
965
976
        self.revisions.add_fallback_versioned_files(repository.revisions)
966
977
        self.signatures.add_fallback_versioned_files(repository.signatures)
 
978
        if self.chk_bytes is not None:
 
979
            self.chk_bytes.add_fallback_versioned_files(repository.chk_bytes)
967
980
 
968
981
    def _check_fallback_repository(self, repository):
969
982
        """Check that this repository can fallback to repository safely.
993
1006
                % (inv.revision_id, revision_id))
994
1007
        if inv.root is None:
995
1008
            raise AssertionError()
 
1009
        return self._add_inventory_checked(revision_id, inv, parents)
 
1010
 
 
1011
    def _add_inventory_checked(self, revision_id, inv, parents):
 
1012
        """Add inv to the repository after checking the inputs.
 
1013
 
 
1014
        This function can be overridden to allow different inventory styles.
 
1015
 
 
1016
        :seealso: add_inventory, for the contract.
 
1017
        """
996
1018
        inv_lines = self._serialise_inventory_to_lines(inv)
997
1019
        return self._inventory_add_lines(revision_id, parents,
998
1020
            inv_lines, check_content=False)
999
1021
 
1000
1022
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1001
 
                               parents):
 
1023
                               parents, basis_inv=None, propagate_caches=False):
1002
1024
        """Add a new inventory expressed as a delta against another revision.
1003
1025
 
1004
1026
        :param basis_revision_id: The inventory id the delta was created
1012
1034
            for repositories that depend on the inventory graph for revision
1013
1035
            graph access, as well as for those that pun ancestry with delta
1014
1036
            compression.
 
1037
        :param basis_inv: The basis inventory if it is already known,
 
1038
            otherwise None.
 
1039
        :param propagate_caches: If True, the caches for this inventory are
 
1040
          copied to and updated for the result if possible.
1015
1041
 
1016
1042
        :returns: (validator, new_inv)
1017
1043
            The validator(which is a sha1 digest, though what is sha'd is
1028
1054
            # inventory implementations may support: A better idiom would be to
1029
1055
            # return a new inventory, but as there is no revision tree cache in
1030
1056
            # repository this is safe for now - RBC 20081013
1031
 
            basis_inv = basis_tree.inventory
 
1057
            if basis_inv is None:
 
1058
                basis_inv = basis_tree.inventory
1032
1059
            basis_inv.apply_delta(delta)
1033
1060
            basis_inv.revision_id = new_revision_id
1034
1061
            return (self.add_inventory(new_revision_id, basis_inv, parents),
1996
2023
                            except KeyError:
1997
2024
                                inv = self.revision_tree(parent_id).inventory
1998
2025
                                inventory_cache[parent_id] = inv
1999
 
                            parent_entry = inv._byid.get(text_key[0], None)
 
2026
                            try:
 
2027
                                parent_entry = inv[text_key[0]]
 
2028
                            except (KeyError, errors.NoSuchId):
 
2029
                                parent_entry = None
2000
2030
                            if parent_entry is not None:
2001
2031
                                parent_text_key = (
2002
2032
                                    text_key[0], parent_entry.revision)
2027
2057
            versions).  knit-kind is one of 'file', 'inventory', 'signatures',
2028
2058
            'revisions'.  file-id is None unless knit-kind is 'file'.
2029
2059
        """
 
2060
        for result in self._find_file_keys_to_fetch(revision_ids, _files_pb):
 
2061
            yield result
 
2062
        del _files_pb
 
2063
        for result in self._find_non_file_keys_to_fetch(revision_ids):
 
2064
            yield result
 
2065
 
 
2066
    def _find_file_keys_to_fetch(self, revision_ids, pb):
2030
2067
        # XXX: it's a bit weird to control the inventory weave caching in this
2031
2068
        # generator.  Ideally the caching would be done in fetch.py I think.  Or
2032
2069
        # maybe this generator should explicitly have the contract that it
2039
2076
        count = 0
2040
2077
        num_file_ids = len(file_ids)
2041
2078
        for file_id, altered_versions in file_ids.iteritems():
2042
 
            if _files_pb is not None:
2043
 
                _files_pb.update("fetch texts", count, num_file_ids)
 
2079
            if pb is not None:
 
2080
                pb.update("fetch texts", count, num_file_ids)
2044
2081
            count += 1
2045
2082
            yield ("file", file_id, altered_versions)
2046
 
        # We're done with the files_pb.  Note that it finished by the caller,
2047
 
        # just as it was created by the caller.
2048
 
        del _files_pb
2049
2083
 
 
2084
    def _find_non_file_keys_to_fetch(self, revision_ids):
2050
2085
        # inventory
2051
2086
        yield ("inventory", None, revision_ids)
2052
2087
 
2492
2527
    """
2493
2528
    repository.start_write_group()
2494
2529
    try:
 
2530
        inventory_cache = lru_cache.LRUCache(10)
2495
2531
        for n, (revision, revision_tree, signature) in enumerate(iterable):
2496
 
            _install_revision(repository, revision, revision_tree, signature)
 
2532
            _install_revision(repository, revision, revision_tree, signature,
 
2533
                inventory_cache)
2497
2534
            if pb is not None:
2498
2535
                pb.update('Transferring revisions', n + 1, num_revisions)
2499
2536
    except:
2503
2540
        repository.commit_write_group()
2504
2541
 
2505
2542
 
2506
 
def _install_revision(repository, rev, revision_tree, signature):
 
2543
def _install_revision(repository, rev, revision_tree, signature,
 
2544
    inventory_cache):
2507
2545
    """Install all revision data into a repository."""
2508
2546
    present_parents = []
2509
2547
    parent_trees = {}
2546
2584
        repository.texts.add_lines(text_key, text_parents, lines)
2547
2585
    try:
2548
2586
        # install the inventory
2549
 
        repository.add_inventory(rev.revision_id, inv, present_parents)
 
2587
        if repository._format._commit_inv_deltas and len(rev.parent_ids):
 
2588
            # Cache this inventory
 
2589
            inventory_cache[rev.revision_id] = inv
 
2590
            try:
 
2591
                basis_inv = inventory_cache[rev.parent_ids[0]]
 
2592
            except KeyError:
 
2593
                repository.add_inventory(rev.revision_id, inv, present_parents)
 
2594
            else:
 
2595
                delta = inv._make_delta(basis_inv)
 
2596
                repository.add_inventory_by_delta(rev.parent_ids[0], delta,
 
2597
                    rev.revision_id, present_parents)
 
2598
        else:
 
2599
            repository.add_inventory(rev.revision_id, inv, present_parents)
2550
2600
    except errors.RevisionAlreadyPresent:
2551
2601
        pass
2552
2602
    if signature is not None:
2659
2709
    # Can this repository be given external locations to lookup additional
2660
2710
    # data. Set to True or False in derived classes.
2661
2711
    supports_external_lookups = None
 
2712
    # Does this format support CHK bytestring lookups. Set to True or False in
 
2713
    # derived classes.
 
2714
    supports_chks = None
 
2715
    # Should commit add an inventory, or an inventory delta to the repository.
 
2716
    _commit_inv_deltas = True
2662
2717
    # What order should fetch operations request streams in?
2663
2718
    # The default is unordered as that is the cheapest for an origin to
2664
2719
    # provide.
2932
2987
    )
2933
2988
 
2934
2989
# Development formats.
2935
 
# 1.7->1.8 go below here
2936
 
format_registry.register_lazy(
2937
 
    "Bazaar development format 2 (needs bzr.dev from before 1.8)\n",
2938
 
    'bzrlib.repofmt.pack_repo',
2939
 
    'RepositoryFormatPackDevelopment2',
2940
 
    )
 
2990
# Obsolete but kept pending a CHK based subtree format.
2941
2991
format_registry.register_lazy(
2942
2992
    ("Bazaar development format 2 with subtree support "
2943
2993
        "(needs bzr.dev from before 1.8)\n"),
2945
2995
    'RepositoryFormatPackDevelopment2Subtree',
2946
2996
    )
2947
2997
 
 
2998
# 1.14->1.16 go below here
 
2999
format_registry.register_lazy(
 
3000
    'Bazaar development format - group compression and chk inventory'
 
3001
        ' (needs bzr.dev from 1.14)\n',
 
3002
    'bzrlib.repofmt.groupcompress_repo',
 
3003
    'RepositoryFormatCHK1',
 
3004
    )
 
3005
 
2948
3006
 
2949
3007
class InterRepository(InterObject):
2950
3008
    """This class represents operations taking place between two repositories.
3313
3371
        We don't test for the stores being of specific types because that
3314
3372
        could lead to confusing results, and there is no need to be
3315
3373
        overly general.
 
3374
 
 
3375
        InterPackRepo does not support CHK based repositories.
3316
3376
        """
3317
3377
        from bzrlib.repofmt.pack_repo import RepositoryFormatPack
 
3378
        from bzrlib.repofmt.groupcompress_repo import RepositoryFormatCHK1
3318
3379
        try:
3319
3380
            are_packs = (isinstance(source._format, RepositoryFormatPack) and
3320
3381
                isinstance(target._format, RepositoryFormatPack))
 
3382
            not_packs = (isinstance(source._format, RepositoryFormatCHK1) or
 
3383
                isinstance(target._format, RepositoryFormatCHK1))
3321
3384
        except AttributeError:
3322
3385
            return False
3323
 
        return are_packs and InterRepository._same_model(source, target)
 
3386
        if not_packs or not are_packs:
 
3387
            return False
 
3388
        return InterRepository._same_model(source, target)
3324
3389
 
3325
3390
    @needs_write_lock
3326
3391
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
3432
3497
        return self.source.revision_ids_to_search_result(result_set)
3433
3498
 
3434
3499
 
3435
 
class InterDifferingSerializer(InterKnitRepo):
 
3500
class InterDifferingSerializer(InterRepository):
3436
3501
 
3437
3502
    @classmethod
3438
3503
    def _get_repo_format_to_test(self):
3441
3506
    @staticmethod
3442
3507
    def is_compatible(source, target):
3443
3508
        """Be compatible with Knit2 source and Knit3 target"""
3444
 
        if source.supports_rich_root() != target.supports_rich_root():
 
3509
        # This is redundant with format.check_conversion_target(), however that
 
3510
        # raises an exception, and we just want to say "False" as in we won't
 
3511
        # support converting between these formats.
 
3512
        if source.supports_rich_root() and not target.supports_rich_root():
3445
3513
            return False
3446
 
        # Ideally, we'd support fetching if the source had no tree references
3447
 
        # even if it supported them...
3448
 
        if (getattr(source, '_format.supports_tree_reference', False) and
3449
 
            not getattr(target, '_format.supports_tree_reference', False)):
 
3514
        if (source._format.supports_tree_reference
 
3515
            and not target._format.supports_tree_reference):
3450
3516
            return False
3451
3517
        return True
3452
3518
 
3469
3535
        deltas.sort()
3470
3536
        return deltas[0][1:]
3471
3537
 
 
3538
    def _get_parent_keys(self, root_key, parent_map):
 
3539
        """Get the parent keys for a given root id."""
 
3540
        root_id, rev_id = root_key
 
3541
        # Include direct parents of the revision, but only if they used
 
3542
        # the same root_id.
 
3543
        parent_keys = []
 
3544
        for parent_id in parent_map[rev_id]:
 
3545
            if parent_id == _mod_revision.NULL_REVISION:
 
3546
                continue
 
3547
            if parent_id not in self._revision_id_to_root_id:
 
3548
                # We probably didn't read this revision, go spend the
 
3549
                # extra effort to actually check
 
3550
                try:
 
3551
                    tree = self.source.revision_tree(parent_id)
 
3552
                except errors.NoSuchRevision:
 
3553
                    # Ghost, fill out _revision_id_to_root_id in case we
 
3554
                    # encounter this again.
 
3555
                    # But set parent_root_id to None since we don't really know
 
3556
                    parent_root_id = None
 
3557
                else:
 
3558
                    parent_root_id = tree.get_root_id()
 
3559
                self._revision_id_to_root_id[parent_id] = None
 
3560
            else:
 
3561
                parent_root_id = self._revision_id_to_root_id[parent_id]
 
3562
            if root_id == parent_root_id or parent_root_id is None:
 
3563
                parent_keys.append((root_id, parent_id))
 
3564
        return tuple(parent_keys)
 
3565
 
 
3566
    def _new_root_data_stream(self, root_keys_to_create, parent_map):
 
3567
        for root_key in root_keys_to_create:
 
3568
            parent_keys = self._get_parent_keys(root_key, parent_map)
 
3569
            yield versionedfile.FulltextContentFactory(root_key,
 
3570
                parent_keys, None, '')
 
3571
 
3472
3572
    def _fetch_batch(self, revision_ids, basis_id, cache):
3473
3573
        """Fetch across a few revisions.
3474
3574
 
3482
3582
        # Walk though all revisions; get inventory deltas, copy referenced
3483
3583
        # texts that delta references, insert the delta, revision and
3484
3584
        # signature.
 
3585
        root_keys_to_create = set()
3485
3586
        text_keys = set()
3486
3587
        pending_deltas = []
3487
3588
        pending_revisions = []
3491
3592
            parent_ids = parent_map.get(current_revision_id, ())
3492
3593
            basis_id, delta = self._get_delta_for_revision(tree, parent_ids,
3493
3594
                                                           basis_id, cache)
 
3595
            if self._converting_to_rich_root:
 
3596
                self._revision_id_to_root_id[current_revision_id] = \
 
3597
                    tree.get_root_id()
3494
3598
            # Find text entries that need to be copied
3495
3599
            for old_path, new_path, file_id, entry in delta:
3496
3600
                if new_path is not None:
3497
 
                    if not (new_path or self.target.supports_rich_root()):
3498
 
                        # We don't copy the text for the root node unless the
3499
 
                        # target supports_rich_root.
3500
 
                        continue
 
3601
                    if not new_path:
 
3602
                        # This is the root
 
3603
                        if not self.target.supports_rich_root():
 
3604
                            # The target doesn't support rich root, so we don't
 
3605
                            # copy
 
3606
                            continue
 
3607
                        if self._converting_to_rich_root:
 
3608
                            # This can't be copied normally, we have to insert
 
3609
                            # it specially
 
3610
                            root_keys_to_create.add((file_id, entry.revision))
 
3611
                            continue
3501
3612
                    text_keys.add((file_id, entry.revision))
3502
3613
            revision = self.source.get_revision(current_revision_id)
3503
3614
            pending_deltas.append((basis_id, delta,
3508
3619
        # Copy file texts
3509
3620
        from_texts = self.source.texts
3510
3621
        to_texts = self.target.texts
 
3622
        if root_keys_to_create:
 
3623
            root_stream = self._new_root_data_stream(root_keys_to_create,
 
3624
                                                     parent_map)
 
3625
            to_texts.insert_record_stream(root_stream)
3511
3626
        to_texts.insert_record_stream(from_texts.get_record_stream(
3512
3627
            text_keys, self.target._format._fetch_order,
3513
3628
            not self.target._format._fetch_uses_deltas))
3560
3675
        """See InterRepository.fetch()."""
3561
3676
        if fetch_spec is not None:
3562
3677
            raise AssertionError("Not implemented yet...")
 
3678
        if (not self.source.supports_rich_root()
 
3679
            and self.target.supports_rich_root()):
 
3680
            self._converting_to_rich_root = True
 
3681
            self._revision_id_to_root_id = {}
 
3682
        else:
 
3683
            self._converting_to_rich_root = False
3563
3684
        revision_ids = self.target.search_missing_revision_ids(self.source,
3564
3685
            revision_id, find_ghosts=find_ghosts).get_keys()
3565
3686
        if not revision_ids:
3566
3687
            return 0, 0
3567
3688
        revision_ids = tsort.topo_sort(
3568
3689
            self.source.get_graph().get_parent_map(revision_ids))
 
3690
        if not revision_ids:
 
3691
            return 0, 0
 
3692
        # Walk though all revisions; get inventory deltas, copy referenced
 
3693
        # texts that delta references, insert the delta, revision and
 
3694
        # signature.
 
3695
        first_rev = self.source.get_revision(revision_ids[0])
3569
3696
        if pb is None:
3570
3697
            my_pb = ui.ui_factory.nested_progress_bar()
3571
3698
            pb = my_pb
3834
3961
                else:
3835
3962
                    self._extract_and_insert_inventories(
3836
3963
                        substream, src_serializer)
 
3964
            elif substream_type == 'chk_bytes':
 
3965
                # XXX: This doesn't support conversions, as it assumes the
 
3966
                #      conversion was done in the fetch code.
 
3967
                self.target_repo.chk_bytes.insert_record_stream(substream)
3837
3968
            elif substream_type == 'revisions':
3838
3969
                # This may fallback to extract-and-insert more often than
3839
3970
                # required if the serializers are different only in terms of
3988
4119
                # know for unselected inventories whether all their required
3989
4120
                # texts are present in the other repository - it could be
3990
4121
                # corrupt.
3991
 
                yield ('inventories', from_weave.get_record_stream(
3992
 
                    [(rev_id,) for rev_id in revs],
3993
 
                    self.inventory_fetch_order(),
3994
 
                    not self.delta_on_metadata()))
 
4122
                for info in self._get_inventory_stream(revs):
 
4123
                    yield info
3995
4124
            elif knit_kind == "signatures":
3996
4125
                # Nothing to do here; this will be taken care of when
3997
4126
                # _fetch_revision_texts happens.
4038
4167
        return (not self.from_repository._format.rich_root_data and
4039
4168
            self.to_format.rich_root_data)
4040
4169
 
 
4170
    def _get_inventory_stream(self, revision_ids):
 
4171
        from_format = self.from_repository._format
 
4172
        if (from_format.supports_chks and self.to_format.supports_chks
 
4173
            and (from_format._serializer == self.to_format._serializer)):
 
4174
            # Both sides support chks, and they use the same serializer, so it
 
4175
            # is safe to transmit the chk pages and inventory pages across
 
4176
            # as-is.
 
4177
            return self._get_chk_inventory_stream(revision_ids)
 
4178
        elif (not from_format.supports_chks):
 
4179
            # Source repository doesn't support chks. So we can transmit the
 
4180
            # inventories 'as-is' and either they are just accepted on the
 
4181
            # target, or the Sink will properly convert it.
 
4182
            return self._get_simple_inventory_stream(revision_ids)
 
4183
        else:
 
4184
            # XXX: Hack to make not-chk->chk fetch: copy the inventories as
 
4185
            #      inventories. Note that this should probably be done somehow
 
4186
            #      as part of bzrlib.repository.StreamSink. Except JAM couldn't
 
4187
            #      figure out how a non-chk repository could possibly handle
 
4188
            #      deserializing an inventory stream from a chk repo, as it
 
4189
            #      doesn't have a way to understand individual pages.
 
4190
            return self._get_convertable_inventory_stream(revision_ids)
 
4191
 
 
4192
    def _get_simple_inventory_stream(self, revision_ids):
 
4193
        from_weave = self.from_repository.inventories
 
4194
        yield ('inventories', from_weave.get_record_stream(
 
4195
            [(rev_id,) for rev_id in revision_ids],
 
4196
            self.inventory_fetch_order(),
 
4197
            not self.delta_on_metadata()))
 
4198
 
 
4199
    def _get_chk_inventory_stream(self, revision_ids):
 
4200
        """Fetch the inventory texts, along with the associated chk maps."""
 
4201
        # We want an inventory outside of the search set, so that we can filter
 
4202
        # out uninteresting chk pages. For now we use
 
4203
        # _find_revision_outside_set, but if we had a Search with cut_revs, we
 
4204
        # could use that instead.
 
4205
        start_rev_id = self.from_repository._find_revision_outside_set(
 
4206
                            revision_ids)
 
4207
        start_rev_key = (start_rev_id,)
 
4208
        inv_keys_to_fetch = [(rev_id,) for rev_id in revision_ids]
 
4209
        if start_rev_id != _mod_revision.NULL_REVISION:
 
4210
            inv_keys_to_fetch.append((start_rev_id,))
 
4211
        # Any repo that supports chk_bytes must also support out-of-order
 
4212
        # insertion. At least, that is how we expect it to work
 
4213
        # We use get_record_stream instead of iter_inventories because we want
 
4214
        # to be able to insert the stream as well. We could instead fetch
 
4215
        # allowing deltas, and then iter_inventories, but we don't know whether
 
4216
        # source or target is more 'local' anway.
 
4217
        inv_stream = self.from_repository.inventories.get_record_stream(
 
4218
            inv_keys_to_fetch, 'unordered',
 
4219
            True) # We need them as full-texts so we can find their references
 
4220
        uninteresting_chk_roots = set()
 
4221
        interesting_chk_roots = set()
 
4222
        def filter_inv_stream(inv_stream):
 
4223
            for idx, record in enumerate(inv_stream):
 
4224
                ### child_pb.update('fetch inv', idx, len(inv_keys_to_fetch))
 
4225
                bytes = record.get_bytes_as('fulltext')
 
4226
                chk_inv = inventory.CHKInventory.deserialise(
 
4227
                    self.from_repository.chk_bytes, bytes, record.key)
 
4228
                if record.key == start_rev_key:
 
4229
                    uninteresting_chk_roots.add(chk_inv.id_to_entry.key())
 
4230
                    p_id_map = chk_inv.parent_id_basename_to_file_id
 
4231
                    if p_id_map is not None:
 
4232
                        uninteresting_chk_roots.add(p_id_map.key())
 
4233
                else:
 
4234
                    yield record
 
4235
                    interesting_chk_roots.add(chk_inv.id_to_entry.key())
 
4236
                    p_id_map = chk_inv.parent_id_basename_to_file_id
 
4237
                    if p_id_map is not None:
 
4238
                        interesting_chk_roots.add(p_id_map.key())
 
4239
        ### pb.update('fetch inventory', 0, 2)
 
4240
        yield ('inventories', filter_inv_stream(inv_stream))
 
4241
        # Now that we have worked out all of the interesting root nodes, grab
 
4242
        # all of the interesting pages and insert them
 
4243
        ### pb.update('fetch inventory', 1, 2)
 
4244
        interesting = chk_map.iter_interesting_nodes(
 
4245
            self.from_repository.chk_bytes, interesting_chk_roots,
 
4246
            uninteresting_chk_roots)
 
4247
        def to_stream_adapter():
 
4248
            """Adapt the iter_interesting_nodes result to a single stream.
 
4249
 
 
4250
            iter_interesting_nodes returns records as it processes them, along
 
4251
            with keys. However, we only want to return the records themselves.
 
4252
            """
 
4253
            for record, items in interesting:
 
4254
                if record is not None:
 
4255
                    yield record
 
4256
        # XXX: We could instead call get_record_stream(records.keys())
 
4257
        #      ATM, this will always insert the records as fulltexts, and
 
4258
        #      requires that you can hang on to records once you have gone
 
4259
        #      on to the next one. Further, it causes the target to
 
4260
        #      recompress the data. Testing shows it to be faster than
 
4261
        #      requesting the records again, though.
 
4262
        yield ('chk_bytes', to_stream_adapter())
 
4263
        ### pb.update('fetch inventory', 2, 2)
 
4264
 
 
4265
    def _get_convertable_inventory_stream(self, revision_ids):
 
4266
        # XXX: One of source or target is using chks, and they don't have
 
4267
        #      compatible serializations. The StreamSink code expects to be
 
4268
        #      able to convert on the target, so we need to put
 
4269
        #      bytes-on-the-wire that can be converted
 
4270
        yield ('inventories', self._stream_invs_as_fulltexts(revision_ids))
 
4271
 
 
4272
    def _stream_invs_as_fulltexts(self, revision_ids):
 
4273
        from_repo = self.from_repository
 
4274
        from_serializer = from_repo._format._serializer
 
4275
        revision_keys = [(rev_id,) for rev_id in revision_ids]
 
4276
        parent_map = from_repo.inventories.get_parent_map(revision_keys)
 
4277
        for inv in self.from_repository.iter_inventories(revision_ids):
 
4278
            # XXX: This is a bit hackish, but it works. Basically,
 
4279
            #      CHKSerializer 'accidentally' supports
 
4280
            #      read/write_inventory_to_string, even though that is never
 
4281
            #      the format that is stored on disk. It *does* give us a
 
4282
            #      single string representation for an inventory, so live with
 
4283
            #      it for now.
 
4284
            #      This would be far better if we had a 'serialized inventory
 
4285
            #      delta' form. Then we could use 'inventory._make_delta', and
 
4286
            #      transmit that. This would both be faster to generate, and
 
4287
            #      result in fewer bytes-on-the-wire.
 
4288
            as_bytes = from_serializer.write_inventory_to_string(inv)
 
4289
            key = (inv.revision_id,)
 
4290
            parent_keys = parent_map.get(key, ())
 
4291
            yield versionedfile.FulltextContentFactory(
 
4292
                key, parent_keys, None, as_bytes)
 
4293