~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2011-01-21 17:22:25 UTC
  • mfrom: (5626.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20110121172225-7mybq84k4ikcails
(vila) Merge 2.3 into trunk including bugfix for #701940 (Vincent
        Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
42
42
    pyutils,
43
43
    revision as _mod_revision,
44
44
    static_tuple,
45
 
    symbol_versioning,
46
45
    trace,
47
46
    tsort,
48
47
    versionedfile,
53
52
from bzrlib.testament import Testament
54
53
""")
55
54
 
56
 
import sys
57
55
from bzrlib import (
58
56
    errors,
59
57
    registry,
 
58
    symbol_versioning,
60
59
    ui,
61
60
    )
62
61
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
176
175
            self._validate_unicode_text(value,
177
176
                                        'revision property (%s)' % (key,))
178
177
 
 
178
    def _ensure_fallback_inventories(self):
 
179
        """Ensure that appropriate inventories are available.
 
180
 
 
181
        This only applies to repositories that are stacked, and is about
 
182
        enusring the stacking invariants. Namely, that for any revision that is
 
183
        present, we either have all of the file content, or we have the parent
 
184
        inventory and the delta file content.
 
185
        """
 
186
        if not self.repository._fallback_repositories:
 
187
            return
 
188
        if not self.repository._format.supports_chks:
 
189
            raise errors.BzrError("Cannot commit directly to a stacked branch"
 
190
                " in pre-2a formats. See "
 
191
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
 
192
        # This is a stacked repo, we need to make sure we have the parent
 
193
        # inventories for the parents.
 
194
        parent_keys = [(p,) for p in self.parents]
 
195
        parent_map = self.repository.inventories._index.get_parent_map(parent_keys)
 
196
        missing_parent_keys = set([pk for pk in parent_keys
 
197
                                       if pk not in parent_map])
 
198
        fallback_repos = list(reversed(self.repository._fallback_repositories))
 
199
        missing_keys = [('inventories', pk[0])
 
200
                        for pk in missing_parent_keys]
 
201
        resume_tokens = []
 
202
        while missing_keys and fallback_repos:
 
203
            fallback_repo = fallback_repos.pop()
 
204
            source = fallback_repo._get_source(self.repository._format)
 
205
            sink = self.repository._get_sink()
 
206
            stream = source.get_stream_for_missing_keys(missing_keys)
 
207
            missing_keys = sink.insert_stream_without_locking(stream,
 
208
                self.repository._format)
 
209
        if missing_keys:
 
210
            raise errors.BzrError('Unable to fill in parent inventories for a'
 
211
                                  ' stacked branch')
 
212
 
179
213
    def commit(self, message):
180
214
        """Make the actual commit.
181
215
 
193
227
        rev.parent_ids = self.parents
194
228
        self.repository.add_revision(self._new_revision_id, rev,
195
229
            self.new_inventory, self._config)
 
230
        self._ensure_fallback_inventories()
196
231
        self.repository.commit_write_group()
197
232
        return self._new_revision_id
198
233
 
1561
1596
        return ret
1562
1597
 
1563
1598
    @needs_read_lock
1564
 
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
1599
    def search_missing_revision_ids(self, other,
 
1600
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
1601
            find_ghosts=True, revision_ids=None, if_present_ids=None):
1565
1602
        """Return the revision ids that other has that this does not.
1566
1603
 
1567
1604
        These are returned in topological order.
1568
1605
 
1569
1606
        revision_id: only return revision ids included by revision_id.
1570
1607
        """
 
1608
        if symbol_versioning.deprecated_passed(revision_id):
 
1609
            symbol_versioning.warn(
 
1610
                'search_missing_revision_ids(revision_id=...) was '
 
1611
                'deprecated in 2.3.  Use revision_ids=[...] instead.',
 
1612
                DeprecationWarning, stacklevel=3)
 
1613
            if revision_ids is not None:
 
1614
                raise AssertionError(
 
1615
                    'revision_ids is mutually exclusive with revision_id')
 
1616
            if revision_id is not None:
 
1617
                revision_ids = [revision_id]
1571
1618
        return InterRepository.get(other, self).search_missing_revision_ids(
1572
 
            revision_id, find_ghosts)
 
1619
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
1620
            if_present_ids=if_present_ids)
1573
1621
 
1574
1622
    @staticmethod
1575
1623
    def open(base):
1761
1809
        :param revprops: Optional dictionary of revision properties.
1762
1810
        :param revision_id: Optional revision id.
1763
1811
        """
1764
 
        if self._fallback_repositories:
1765
 
            raise errors.BzrError("Cannot commit from a lightweight checkout "
1766
 
                "to a stacked branch. See "
 
1812
        if self._fallback_repositories and not self._format.supports_chks:
 
1813
            raise errors.BzrError("Cannot commit directly to a stacked branch"
 
1814
                " in pre-2a formats. See "
1767
1815
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1768
1816
        result = self._commit_builder_class(self, parents, config,
1769
1817
            timestamp, timezone, committer, revprops, revision_id)
2822
2870
        raise NotImplementedError(self.revision_graph_can_have_wrong_parents)
2823
2871
 
2824
2872
 
2825
 
# remove these delegates a while after bzr 0.15
2826
 
def __make_delegated(name, from_module):
2827
 
    def _deprecated_repository_forwarder():
2828
 
        symbol_versioning.warn('%s moved to %s in bzr 0.15'
2829
 
            % (name, from_module),
2830
 
            DeprecationWarning,
2831
 
            stacklevel=2)
2832
 
        try:
2833
 
            return pyutils.get_named_object(from_module, name)
2834
 
        except AttributeError:
2835
 
            raise AttributeError('module %s has no name %s'
2836
 
                    % (sys.modules[from_module], name))
2837
 
    globals()[name] = _deprecated_repository_forwarder
2838
 
 
2839
 
for _name in [
2840
 
        'AllInOneRepository',
2841
 
        'WeaveMetaDirRepository',
2842
 
        'PreSplitOutRepositoryFormat',
2843
 
        'RepositoryFormat4',
2844
 
        'RepositoryFormat5',
2845
 
        'RepositoryFormat6',
2846
 
        'RepositoryFormat7',
2847
 
        ]:
2848
 
    __make_delegated(_name, 'bzrlib.repofmt.weaverepo')
2849
 
 
2850
 
for _name in [
2851
 
        'KnitRepository',
2852
 
        'RepositoryFormatKnit',
2853
 
        'RepositoryFormatKnit1',
2854
 
        ]:
2855
 
    __make_delegated(_name, 'bzrlib.repofmt.knitrepo')
2856
 
 
2857
 
 
2858
2873
def install_revision(repository, rev, revision_tree):
2859
2874
    """Install all revision data into a repository."""
2860
2875
    install_revisions(repository, [(rev, revision_tree, None)])
3079
3094
    supports_tree_reference = None
3080
3095
    # Is the format experimental ?
3081
3096
    experimental = False
 
3097
    # Does this repository format escape funky characters, or does it create files with
 
3098
    # similar names as the versioned files in its contents on disk ?
 
3099
    supports_funky_characters = True
3082
3100
 
3083
3101
    def __repr__(self):
3084
3102
        return "%s()" % self.__class__.__name__
3352
3370
    'bzrlib.repofmt.pack_repo',
3353
3371
    'RepositoryFormatKnitPack6RichRoot',
3354
3372
    )
 
3373
format_registry.register_lazy(
 
3374
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
3375
    'bzrlib.repofmt.groupcompress_repo',
 
3376
    'RepositoryFormat2a',
 
3377
    )
3355
3378
 
3356
3379
# Development formats.
3357
 
# Obsolete but kept pending a CHK based subtree format.
 
3380
# Check their docstrings to see if/when they are obsolete.
3358
3381
format_registry.register_lazy(
3359
3382
    ("Bazaar development format 2 with subtree support "
3360
3383
        "(needs bzr.dev from before 1.8)\n"),
3361
3384
    'bzrlib.repofmt.pack_repo',
3362
3385
    'RepositoryFormatPackDevelopment2Subtree',
3363
3386
    )
3364
 
 
3365
 
# 1.14->1.16 go below here
3366
 
format_registry.register_lazy(
3367
 
    'Bazaar development format - group compression and chk inventory'
3368
 
        ' (needs bzr.dev from 1.14)\n',
3369
 
    'bzrlib.repofmt.groupcompress_repo',
3370
 
    'RepositoryFormatCHK1',
3371
 
    )
3372
 
 
3373
 
format_registry.register_lazy(
3374
 
    'Bazaar development format - chk repository with bencode revision '
3375
 
        'serialization (needs bzr.dev from 1.16)\n',
3376
 
    'bzrlib.repofmt.groupcompress_repo',
3377
 
    'RepositoryFormatCHK2',
3378
 
    )
3379
 
format_registry.register_lazy(
3380
 
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
3381
 
    'bzrlib.repofmt.groupcompress_repo',
3382
 
    'RepositoryFormat2a',
3383
 
    )
3384
3387
format_registry.register_lazy(
3385
3388
    'Bazaar development format 8\n',
3386
3389
    'bzrlib.repofmt.groupcompress_repo',
3445
3448
                               fetch_spec=fetch_spec,
3446
3449
                               find_ghosts=find_ghosts)
3447
3450
 
3448
 
    def _walk_to_common_revisions(self, revision_ids):
 
3451
    def _walk_to_common_revisions(self, revision_ids, if_present_ids=None):
3449
3452
        """Walk out from revision_ids in source to revisions target has.
3450
3453
 
3451
3454
        :param revision_ids: The start point for the search.
3453
3456
        """
3454
3457
        target_graph = self.target.get_graph()
3455
3458
        revision_ids = frozenset(revision_ids)
 
3459
        if if_present_ids:
 
3460
            all_wanted_revs = revision_ids.union(if_present_ids)
 
3461
        else:
 
3462
            all_wanted_revs = revision_ids
3456
3463
        missing_revs = set()
3457
3464
        source_graph = self.source.get_graph()
3458
3465
        # ensure we don't pay silly lookup costs.
3459
 
        searcher = source_graph._make_breadth_first_searcher(revision_ids)
 
3466
        searcher = source_graph._make_breadth_first_searcher(all_wanted_revs)
3460
3467
        null_set = frozenset([_mod_revision.NULL_REVISION])
3461
3468
        searcher_exhausted = False
3462
3469
        while True:
3498
3505
        return searcher.get_result()
3499
3506
 
3500
3507
    @needs_read_lock
3501
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
3508
    def search_missing_revision_ids(self,
 
3509
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
3510
            find_ghosts=True, revision_ids=None, if_present_ids=None):
3502
3511
        """Return the revision ids that source has that target does not.
3503
3512
 
3504
3513
        :param revision_id: only return revision ids included by this
3505
 
                            revision_id.
 
3514
            revision_id.
 
3515
        :param revision_ids: return revision ids included by these
 
3516
            revision_ids.  NoSuchRevision will be raised if any of these
 
3517
            revisions are not present.
 
3518
        :param if_present_ids: like revision_ids, but will not cause
 
3519
            NoSuchRevision if any of these are absent, instead they will simply
 
3520
            not be in the result.  This is useful for e.g. finding revisions
 
3521
            to fetch for tags, which may reference absent revisions.
3506
3522
        :param find_ghosts: If True find missing revisions in deep history
3507
3523
            rather than just finding the surface difference.
3508
3524
        :return: A bzrlib.graph.SearchResult.
3509
3525
        """
 
3526
        if symbol_versioning.deprecated_passed(revision_id):
 
3527
            symbol_versioning.warn(
 
3528
                'search_missing_revision_ids(revision_id=...) was '
 
3529
                'deprecated in 2.3.  Use revision_ids=[...] instead.',
 
3530
                DeprecationWarning, stacklevel=2)
 
3531
            if revision_ids is not None:
 
3532
                raise AssertionError(
 
3533
                    'revision_ids is mutually exclusive with revision_id')
 
3534
            if revision_id is not None:
 
3535
                revision_ids = [revision_id]
 
3536
        del revision_id
3510
3537
        # stop searching at found target revisions.
3511
 
        if not find_ghosts and revision_id is not None:
3512
 
            return self._walk_to_common_revisions([revision_id])
 
3538
        if not find_ghosts and (revision_ids is not None or if_present_ids is
 
3539
                not None):
 
3540
            return self._walk_to_common_revisions(revision_ids,
 
3541
                    if_present_ids=if_present_ids)
3513
3542
        # generic, possibly worst case, slow code path.
3514
3543
        target_ids = set(self.target.all_revision_ids())
3515
 
        if revision_id is not None:
3516
 
            source_ids = self.source.get_ancestry(revision_id)
3517
 
            if source_ids[0] is not None:
3518
 
                raise AssertionError()
3519
 
            source_ids.pop(0)
3520
 
        else:
3521
 
            source_ids = self.source.all_revision_ids()
 
3544
        source_ids = self._present_source_revisions_for(
 
3545
            revision_ids, if_present_ids)
3522
3546
        result_set = set(source_ids).difference(target_ids)
3523
3547
        return self.source.revision_ids_to_search_result(result_set)
3524
3548
 
 
3549
    def _present_source_revisions_for(self, revision_ids, if_present_ids=None):
 
3550
        """Returns set of all revisions in ancestry of revision_ids present in
 
3551
        the source repo.
 
3552
 
 
3553
        :param revision_ids: if None, all revisions in source are returned.
 
3554
        :param if_present_ids: like revision_ids, but if any/all of these are
 
3555
            absent no error is raised.
 
3556
        """
 
3557
        if revision_ids is not None or if_present_ids is not None:
 
3558
            # First, ensure all specified revisions exist.  Callers expect
 
3559
            # NoSuchRevision when they pass absent revision_ids here.
 
3560
            if revision_ids is None:
 
3561
                revision_ids = set()
 
3562
            if if_present_ids is None:
 
3563
                if_present_ids = set()
 
3564
            revision_ids = set(revision_ids)
 
3565
            if_present_ids = set(if_present_ids)
 
3566
            all_wanted_ids = revision_ids.union(if_present_ids)
 
3567
            graph = self.source.get_graph()
 
3568
            present_revs = set(graph.get_parent_map(all_wanted_ids))
 
3569
            missing = revision_ids.difference(present_revs)
 
3570
            if missing:
 
3571
                raise errors.NoSuchRevision(self.source, missing.pop())
 
3572
            found_ids = all_wanted_ids.intersection(present_revs)
 
3573
            source_ids = [rev_id for (rev_id, parents) in
 
3574
                          graph.iter_ancestry(found_ids)
 
3575
                          if rev_id != _mod_revision.NULL_REVISION
 
3576
                          and parents is not None]
 
3577
        else:
 
3578
            source_ids = self.source.all_revision_ids()
 
3579
        return set(source_ids)
 
3580
 
3525
3581
    @staticmethod
3526
3582
    def _same_model(source, target):
3527
3583
        """True if source and target have the same data representation.
3568
3624
        return InterRepository._same_model(source, target)
3569
3625
 
3570
3626
 
3571
 
class InterWeaveRepo(InterSameDataRepository):
3572
 
    """Optimised code paths between Weave based repositories.
3573
 
 
3574
 
    This should be in bzrlib/repofmt/weaverepo.py but we have not yet
3575
 
    implemented lazy inter-object optimisation.
3576
 
    """
3577
 
 
3578
 
    @classmethod
3579
 
    def _get_repo_format_to_test(self):
3580
 
        from bzrlib.repofmt import weaverepo
3581
 
        return weaverepo.RepositoryFormat7()
3582
 
 
3583
 
    @staticmethod
3584
 
    def is_compatible(source, target):
3585
 
        """Be compatible with known Weave formats.
3586
 
 
3587
 
        We don't test for the stores being of specific types because that
3588
 
        could lead to confusing results, and there is no need to be
3589
 
        overly general.
3590
 
        """
3591
 
        from bzrlib.repofmt.weaverepo import (
3592
 
                RepositoryFormat5,
3593
 
                RepositoryFormat6,
3594
 
                RepositoryFormat7,
3595
 
                )
3596
 
        try:
3597
 
            return (isinstance(source._format, (RepositoryFormat5,
3598
 
                                                RepositoryFormat6,
3599
 
                                                RepositoryFormat7)) and
3600
 
                    isinstance(target._format, (RepositoryFormat5,
3601
 
                                                RepositoryFormat6,
3602
 
                                                RepositoryFormat7)))
3603
 
        except AttributeError:
3604
 
            return False
3605
 
 
3606
 
    @needs_write_lock
3607
 
    def copy_content(self, revision_id=None):
3608
 
        """See InterRepository.copy_content()."""
3609
 
        # weave specific optimised path:
3610
 
        try:
3611
 
            self.target.set_make_working_trees(self.source.make_working_trees())
3612
 
        except (errors.RepositoryUpgradeRequired, NotImplemented):
3613
 
            pass
3614
 
        # FIXME do not peek!
3615
 
        if self.source._transport.listable():
3616
 
            pb = ui.ui_factory.nested_progress_bar()
3617
 
            try:
3618
 
                self.target.texts.insert_record_stream(
3619
 
                    self.source.texts.get_record_stream(
3620
 
                        self.source.texts.keys(), 'topological', False))
3621
 
                pb.update('Copying inventory', 0, 1)
3622
 
                self.target.inventories.insert_record_stream(
3623
 
                    self.source.inventories.get_record_stream(
3624
 
                        self.source.inventories.keys(), 'topological', False))
3625
 
                self.target.signatures.insert_record_stream(
3626
 
                    self.source.signatures.get_record_stream(
3627
 
                        self.source.signatures.keys(),
3628
 
                        'unordered', True))
3629
 
                self.target.revisions.insert_record_stream(
3630
 
                    self.source.revisions.get_record_stream(
3631
 
                        self.source.revisions.keys(),
3632
 
                        'topological', True))
3633
 
            finally:
3634
 
                pb.finished()
3635
 
        else:
3636
 
            self.target.fetch(self.source, revision_id=revision_id)
3637
 
 
3638
 
    @needs_read_lock
3639
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3640
 
        """See InterRepository.missing_revision_ids()."""
3641
 
        # we want all revisions to satisfy revision_id in source.
3642
 
        # but we don't want to stat every file here and there.
3643
 
        # we want then, all revisions other needs to satisfy revision_id
3644
 
        # checked, but not those that we have locally.
3645
 
        # so the first thing is to get a subset of the revisions to
3646
 
        # satisfy revision_id in source, and then eliminate those that
3647
 
        # we do already have.
3648
 
        # this is slow on high latency connection to self, but as this
3649
 
        # disk format scales terribly for push anyway due to rewriting
3650
 
        # inventory.weave, this is considered acceptable.
3651
 
        # - RBC 20060209
3652
 
        if revision_id is not None:
3653
 
            source_ids = self.source.get_ancestry(revision_id)
3654
 
            if source_ids[0] is not None:
3655
 
                raise AssertionError()
3656
 
            source_ids.pop(0)
3657
 
        else:
3658
 
            source_ids = self.source._all_possible_ids()
3659
 
        source_ids_set = set(source_ids)
3660
 
        # source_ids is the worst possible case we may need to pull.
3661
 
        # now we want to filter source_ids against what we actually
3662
 
        # have in target, but don't try to check for existence where we know
3663
 
        # we do not have a revision as that would be pointless.
3664
 
        target_ids = set(self.target._all_possible_ids())
3665
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3666
 
        actually_present_revisions = set(
3667
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3668
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3669
 
        if revision_id is not None:
3670
 
            # we used get_ancestry to determine source_ids then we are assured all
3671
 
            # revisions referenced are present as they are installed in topological order.
3672
 
            # and the tip revision was validated by get_ancestry.
3673
 
            result_set = required_revisions
3674
 
        else:
3675
 
            # if we just grabbed the possibly available ids, then
3676
 
            # we only have an estimate of whats available and need to validate
3677
 
            # that against the revision records.
3678
 
            result_set = set(
3679
 
                self.source._eliminate_revisions_not_present(required_revisions))
3680
 
        return self.source.revision_ids_to_search_result(result_set)
3681
 
 
3682
 
 
3683
 
class InterKnitRepo(InterSameDataRepository):
3684
 
    """Optimised code paths between Knit based repositories."""
3685
 
 
3686
 
    @classmethod
3687
 
    def _get_repo_format_to_test(self):
3688
 
        from bzrlib.repofmt import knitrepo
3689
 
        return knitrepo.RepositoryFormatKnit1()
3690
 
 
3691
 
    @staticmethod
3692
 
    def is_compatible(source, target):
3693
 
        """Be compatible with known Knit formats.
3694
 
 
3695
 
        We don't test for the stores being of specific types because that
3696
 
        could lead to confusing results, and there is no need to be
3697
 
        overly general.
3698
 
        """
3699
 
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
3700
 
        try:
3701
 
            are_knits = (isinstance(source._format, RepositoryFormatKnit) and
3702
 
                isinstance(target._format, RepositoryFormatKnit))
3703
 
        except AttributeError:
3704
 
            return False
3705
 
        return are_knits and InterRepository._same_model(source, target)
3706
 
 
3707
 
    @needs_read_lock
3708
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3709
 
        """See InterRepository.missing_revision_ids()."""
3710
 
        if revision_id is not None:
3711
 
            source_ids = self.source.get_ancestry(revision_id)
3712
 
            if source_ids[0] is not None:
3713
 
                raise AssertionError()
3714
 
            source_ids.pop(0)
3715
 
        else:
3716
 
            source_ids = self.source.all_revision_ids()
3717
 
        source_ids_set = set(source_ids)
3718
 
        # source_ids is the worst possible case we may need to pull.
3719
 
        # now we want to filter source_ids against what we actually
3720
 
        # have in target, but don't try to check for existence where we know
3721
 
        # we do not have a revision as that would be pointless.
3722
 
        target_ids = set(self.target.all_revision_ids())
3723
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3724
 
        actually_present_revisions = set(
3725
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3726
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3727
 
        if revision_id is not None:
3728
 
            # we used get_ancestry to determine source_ids then we are assured all
3729
 
            # revisions referenced are present as they are installed in topological order.
3730
 
            # and the tip revision was validated by get_ancestry.
3731
 
            result_set = required_revisions
3732
 
        else:
3733
 
            # if we just grabbed the possibly available ids, then
3734
 
            # we only have an estimate of whats available and need to validate
3735
 
            # that against the revision records.
3736
 
            result_set = set(
3737
 
                self.source._eliminate_revisions_not_present(required_revisions))
3738
 
        return self.source.revision_ids_to_search_result(result_set)
3739
 
 
3740
 
 
3741
3627
class InterDifferingSerializer(InterRepository):
3742
3628
 
3743
3629
    @classmethod
4015
3901
            fetch_spec=None):
4016
3902
        """See InterRepository.fetch()."""
4017
3903
        if fetch_spec is not None:
4018
 
            raise AssertionError("Not implemented yet...")
 
3904
            if (isinstance(fetch_spec, graph.NotInOtherForRevs) and
 
3905
                    len(fetch_spec.required_ids) == 1 and not
 
3906
                    fetch_spec.if_present_ids):
 
3907
                revision_id = list(fetch_spec.required_ids)[0]
 
3908
                del fetch_spec
 
3909
            else:
 
3910
                raise AssertionError("Not implemented yet...")
4019
3911
        ui.ui_factory.warn_experimental_format_fetch(self)
4020
3912
        if (not self.source.supports_rich_root()
4021
3913
            and self.target.supports_rich_root()):
4028
3920
            ui.ui_factory.show_user_warning('cross_format_fetch',
4029
3921
                from_format=self.source._format,
4030
3922
                to_format=self.target._format)
 
3923
        if revision_id:
 
3924
            search_revision_ids = [revision_id]
 
3925
        else:
 
3926
            search_revision_ids = None
4031
3927
        revision_ids = self.target.search_missing_revision_ids(self.source,
4032
 
            revision_id, find_ghosts=find_ghosts).get_keys()
 
3928
            revision_ids=search_revision_ids, find_ghosts=find_ghosts).get_keys()
4033
3929
        if not revision_ids:
4034
3930
            return 0, 0
4035
3931
        revision_ids = tsort.topo_sort(
4079
3975
 
4080
3976
InterRepository.register_optimiser(InterDifferingSerializer)
4081
3977
InterRepository.register_optimiser(InterSameDataRepository)
4082
 
InterRepository.register_optimiser(InterWeaveRepo)
4083
 
InterRepository.register_optimiser(InterKnitRepo)
4084
3978
 
4085
3979
 
4086
3980
class CopyConverter(object):
4274
4168
                is_resume = False
4275
4169
            try:
4276
4170
                # locked_insert_stream performs a commit|suspend.
4277
 
                return self._locked_insert_stream(stream, src_format,
4278
 
                    is_resume)
 
4171
                missing_keys = self.insert_stream_without_locking(stream,
 
4172
                                    src_format, is_resume)
 
4173
                if missing_keys:
 
4174
                    # suspend the write group and tell the caller what we is
 
4175
                    # missing. We know we can suspend or else we would not have
 
4176
                    # entered this code path. (All repositories that can handle
 
4177
                    # missing keys can handle suspending a write group).
 
4178
                    write_group_tokens = self.target_repo.suspend_write_group()
 
4179
                    return write_group_tokens, missing_keys
 
4180
                hint = self.target_repo.commit_write_group()
 
4181
                to_serializer = self.target_repo._format._serializer
 
4182
                src_serializer = src_format._serializer
 
4183
                if (to_serializer != src_serializer and
 
4184
                    self.target_repo._format.pack_compresses):
 
4185
                    self.target_repo.pack(hint=hint)
 
4186
                return [], set()
4279
4187
            except:
4280
4188
                self.target_repo.abort_write_group(suppress_errors=True)
4281
4189
                raise
4282
4190
        finally:
4283
4191
            self.target_repo.unlock()
4284
4192
 
4285
 
    def _locked_insert_stream(self, stream, src_format, is_resume):
 
4193
    def insert_stream_without_locking(self, stream, src_format,
 
4194
                                      is_resume=False):
 
4195
        """Insert a stream's content into the target repository.
 
4196
 
 
4197
        This assumes that you already have a locked repository and an active
 
4198
        write group.
 
4199
 
 
4200
        :param src_format: a bzr repository format.
 
4201
        :param is_resume: Passed down to get_missing_parent_inventories to
 
4202
            indicate if we should be checking for missing texts at the same
 
4203
            time.
 
4204
 
 
4205
        :return: A set of keys that are missing.
 
4206
        """
 
4207
        if not self.target_repo.is_write_locked():
 
4208
            raise errors.ObjectNotLocked(self)
 
4209
        if not self.target_repo.is_in_write_group():
 
4210
            raise errors.BzrError('you must already be in a write group')
4286
4211
        to_serializer = self.target_repo._format._serializer
4287
4212
        src_serializer = src_format._serializer
4288
4213
        new_pack = None
4367
4292
            # cannot even attempt suspending, and missing would have failed
4368
4293
            # during stream insertion.
4369
4294
            missing_keys = set()
4370
 
        else:
4371
 
            if missing_keys:
4372
 
                # suspend the write group and tell the caller what we is
4373
 
                # missing. We know we can suspend or else we would not have
4374
 
                # entered this code path. (All repositories that can handle
4375
 
                # missing keys can handle suspending a write group).
4376
 
                write_group_tokens = self.target_repo.suspend_write_group()
4377
 
                return write_group_tokens, missing_keys
4378
 
        hint = self.target_repo.commit_write_group()
4379
 
        if (to_serializer != src_serializer and
4380
 
            self.target_repo._format.pack_compresses):
4381
 
            self.target_repo.pack(hint=hint)
4382
 
        return [], set()
 
4295
        return missing_keys
4383
4296
 
4384
4297
    def _extract_and_insert_inventory_deltas(self, substream, serializer):
4385
4298
        target_rich_root = self.target_repo._format.rich_root_data