/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

Don't add a new verb; instead just teach the client to fallback if it gets a BadSearch error.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
    check,
26
26
    chk_map,
27
27
    config,
 
28
    controldir,
28
29
    debug,
29
 
    errors,
30
30
    fetch as _mod_fetch,
31
31
    fifo_cache,
32
32
    generate_ids,
39
39
    lockdir,
40
40
    lru_cache,
41
41
    osutils,
 
42
    pyutils,
42
43
    revision as _mod_revision,
43
44
    static_tuple,
44
 
    symbol_versioning,
45
45
    trace,
46
46
    tsort,
47
 
    ui,
48
47
    versionedfile,
49
48
    )
50
49
from bzrlib.bundle import serializer
53
52
from bzrlib.testament import Testament
54
53
""")
55
54
 
 
55
import sys
 
56
from bzrlib import (
 
57
    errors,
 
58
    registry,
 
59
    symbol_versioning,
 
60
    ui,
 
61
    )
56
62
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
57
63
from bzrlib.inter import InterObject
58
64
from bzrlib.inventory import (
61
67
    ROOT_ID,
62
68
    entry_factory,
63
69
    )
64
 
from bzrlib.lock import _RelockDebugMixin
65
 
from bzrlib import registry
 
70
from bzrlib.recordcounter import RecordCounter
 
71
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
66
72
from bzrlib.trace import (
67
73
    log_exception_quietly, note, mutter, mutter_callsite, warning)
68
74
 
71
77
_deprecation_warning_done = False
72
78
 
73
79
 
 
80
class IsInWriteGroupError(errors.InternalBzrError):
 
81
 
 
82
    _fmt = "May not refresh_data of repo %(repo)s while in a write group."
 
83
 
 
84
    def __init__(self, repo):
 
85
        errors.InternalBzrError.__init__(self, repo=repo)
 
86
 
 
87
 
74
88
class CommitBuilder(object):
75
89
    """Provides an interface to build up a commit.
76
90
 
101
115
 
102
116
        if committer is None:
103
117
            self._committer = self._config.username()
 
118
        elif not isinstance(committer, unicode):
 
119
            self._committer = committer.decode() # throw if non-ascii
104
120
        else:
105
121
            self._committer = committer
106
122
 
231
247
 
232
248
    def _gen_revision_id(self):
233
249
        """Return new revision-id."""
234
 
        return generate_ids.gen_revision_id(self._config.username(),
235
 
                                            self._timestamp)
 
250
        return generate_ids.gen_revision_id(self._committer, self._timestamp)
236
251
 
237
252
    def _generate_revision_if_needed(self):
238
253
        """Create a revision id if None was supplied.
278
293
 
279
294
        :param tree: The tree which is being committed.
280
295
        """
281
 
        # NB: if there are no parents then this method is not called, so no
282
 
        # need to guard on parents having length.
 
296
        if len(self.parents) == 0:
 
297
            raise errors.RootMissing()
283
298
        entry = entry_factory['directory'](tree.path2id(''), '',
284
299
            None)
285
300
        entry.revision = self._new_revision_id
423
438
            else:
424
439
                # we don't need to commit this, because the caller already
425
440
                # determined that an existing revision of this file is
426
 
                # appropriate. If its not being considered for committing then
 
441
                # appropriate. If it's not being considered for committing then
427
442
                # it and all its parents to the root must be unaltered so
428
443
                # no-change against the basis.
429
444
                if ie.revision == self._new_revision_id:
745
760
                    # after iter_changes examines and decides it has changed,
746
761
                    # we will unconditionally record a new version even if some
747
762
                    # other process reverts it while commit is running (with
748
 
                    # the revert happening after iter_changes did it's
 
763
                    # the revert happening after iter_changes did its
749
764
                    # examination).
750
765
                    if change[7][1]:
751
766
                        entry.executable = True
860
875
        # versioned roots do not change unless the tree found a change.
861
876
 
862
877
 
 
878
class RepositoryWriteLockResult(LogicalLockResult):
 
879
    """The result of write locking a repository.
 
880
 
 
881
    :ivar repository_token: The token obtained from the underlying lock, or
 
882
        None.
 
883
    :ivar unlock: A callable which will unlock the lock.
 
884
    """
 
885
 
 
886
    def __init__(self, unlock, repository_token):
 
887
        LogicalLockResult.__init__(self, unlock)
 
888
        self.repository_token = repository_token
 
889
 
 
890
    def __repr__(self):
 
891
        return "RepositoryWriteLockResult(%s, %s)" % (self.repository_token,
 
892
            self.unlock)
 
893
 
 
894
 
863
895
######################################################################
864
896
# Repositories
865
897
 
866
898
 
867
 
class Repository(_RelockDebugMixin, bzrdir.ControlComponent):
 
899
class Repository(_RelockDebugMixin, controldir.ControlComponent):
868
900
    """Repository holding history for one or more branches.
869
901
 
870
902
    The repository holds and retrieves historical information including
917
949
        pointing to .bzr/repository.
918
950
    """
919
951
 
920
 
    # What class to use for a CommitBuilder. Often its simpler to change this
 
952
    # What class to use for a CommitBuilder. Often it's simpler to change this
921
953
    # in a Repository class subclass rather than to override
922
954
    # get_commit_builder.
923
955
    _commit_builder_class = CommitBuilder
1018
1050
                " id and insertion revid (%r, %r)"
1019
1051
                % (inv.revision_id, revision_id))
1020
1052
        if inv.root is None:
1021
 
            raise AssertionError()
 
1053
            raise errors.RootMissing()
1022
1054
        return self._add_inventory_checked(revision_id, inv, parents)
1023
1055
 
1024
1056
    def _add_inventory_checked(self, revision_id, inv, parents):
1376
1408
        data during reads, and allows a 'write_group' to be obtained. Write
1377
1409
        groups must be used for actual data insertion.
1378
1410
 
 
1411
        A token should be passed in if you know that you have locked the object
 
1412
        some other way, and need to synchronise this object's state with that
 
1413
        fact.
 
1414
 
 
1415
        XXX: this docstring is duplicated in many places, e.g. lockable_files.py
 
1416
 
1379
1417
        :param token: if this is already locked, then lock_write will fail
1380
1418
            unless the token matches the existing lock.
1381
1419
        :returns: a token if this instance supports tokens, otherwise None.
1384
1422
        :raises MismatchedToken: if the specified token doesn't match the token
1385
1423
            of the existing lock.
1386
1424
        :seealso: start_write_group.
1387
 
 
1388
 
        A token should be passed in if you know that you have locked the object
1389
 
        some other way, and need to synchronise this object's state with that
1390
 
        fact.
1391
 
 
1392
 
        XXX: this docstring is duplicated in many places, e.g. lockable_files.py
 
1425
        :return: A RepositoryWriteLockResult.
1393
1426
        """
1394
1427
        locked = self.is_locked()
1395
 
        result = self.control_files.lock_write(token=token)
 
1428
        token = self.control_files.lock_write(token=token)
1396
1429
        if not locked:
1397
1430
            self._warn_if_deprecated()
1398
1431
            self._note_lock('w')
1400
1433
                # Writes don't affect fallback repos
1401
1434
                repo.lock_read()
1402
1435
            self._refresh_data()
1403
 
        return result
 
1436
        return RepositoryWriteLockResult(self.unlock, token)
1404
1437
 
1405
1438
    def lock_read(self):
 
1439
        """Lock the repository for read operations.
 
1440
 
 
1441
        :return: An object with an unlock method which will release the lock
 
1442
            obtained.
 
1443
        """
1406
1444
        locked = self.is_locked()
1407
1445
        self.control_files.lock_read()
1408
1446
        if not locked:
1411
1449
            for repo in self._fallback_repositories:
1412
1450
                repo.lock_read()
1413
1451
            self._refresh_data()
 
1452
        return LogicalLockResult(self.unlock)
1414
1453
 
1415
1454
    def get_physical_lock_status(self):
1416
1455
        return self.control_files.get_physical_lock_status()
1522
1561
        return ret
1523
1562
 
1524
1563
    @needs_read_lock
1525
 
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
1564
    def search_missing_revision_ids(self, other,
 
1565
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
1566
            find_ghosts=True, revision_ids=None):
1526
1567
        """Return the revision ids that other has that this does not.
1527
1568
 
1528
1569
        These are returned in topological order.
1529
1570
 
1530
1571
        revision_id: only return revision ids included by revision_id.
1531
1572
        """
 
1573
        if symbol_versioning.deprecated_passed(revision_id):
 
1574
            symbol_versioning.warn(
 
1575
                'search_missing_revision_ids(revision_id=...) was '
 
1576
                'deprecated in 2.3.  Use revision_ids=[...] instead.',
 
1577
                DeprecationWarning, stacklevel=3)
 
1578
            if revision_ids is not None:
 
1579
                raise AssertionError(
 
1580
                    'revision_ids is mutually exclusive with revision_id')
 
1581
            if revision_id is not None:
 
1582
                revision_ids = [revision_id]
1532
1583
        return InterRepository.get(other, self).search_missing_revision_ids(
1533
 
            revision_id, find_ghosts)
 
1584
            find_ghosts=find_ghosts, revision_ids=revision_ids)
1534
1585
 
1535
1586
    @staticmethod
1536
1587
    def open(base):
1634
1685
        return missing_keys
1635
1686
 
1636
1687
    def refresh_data(self):
1637
 
        """Re-read any data needed to to synchronise with disk.
 
1688
        """Re-read any data needed to synchronise with disk.
1638
1689
 
1639
1690
        This method is intended to be called after another repository instance
1640
1691
        (such as one used by a smart server) has inserted data into the
1641
 
        repository. It may not be called during a write group, but may be
1642
 
        called at any other time.
 
1692
        repository. On all repositories this will work outside of write groups.
 
1693
        Some repository formats (pack and newer for bzrlib native formats)
 
1694
        support refresh_data inside write groups. If called inside a write
 
1695
        group on a repository that does not support refreshing in a write group
 
1696
        IsInWriteGroupError will be raised.
1643
1697
        """
1644
 
        if self.is_in_write_group():
1645
 
            raise errors.InternalBzrError(
1646
 
                "May not refresh_data while in a write group.")
1647
1698
        self._refresh_data()
1648
1699
 
1649
1700
    def resume_write_group(self, tokens):
1688
1739
                "May not fetch while in a write group.")
1689
1740
        # fast path same-url fetch operations
1690
1741
        # TODO: lift out to somewhere common with RemoteRepository
1691
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401646>
 
1742
        # <https://bugs.launchpad.net/bzr/+bug/401646>
1692
1743
        if (self.has_same_location(source)
1693
1744
            and fetch_spec is None
1694
1745
            and self._has_same_fallbacks(source)):
2476
2527
            ancestors will be traversed.
2477
2528
        """
2478
2529
        graph = self.get_graph()
2479
 
        next_id = revision_id
2480
 
        while True:
2481
 
            if next_id in (None, _mod_revision.NULL_REVISION):
2482
 
                return
2483
 
            try:
2484
 
                parents = graph.get_parent_map([next_id])[next_id]
2485
 
            except KeyError:
2486
 
                raise errors.RevisionNotPresent(next_id, self)
2487
 
            yield next_id
2488
 
            if len(parents) == 0:
2489
 
                return
2490
 
            else:
2491
 
                next_id = parents[0]
 
2530
        stop_revisions = (None, _mod_revision.NULL_REVISION)
 
2531
        return graph.iter_lefthand_ancestry(revision_id, stop_revisions)
2492
2532
 
2493
2533
    def is_shared(self):
2494
2534
        """Return True if this repository is flagged as a shared repository."""
2595
2635
        types it should be a no-op that just returns.
2596
2636
 
2597
2637
        This stub method does not require a lock, but subclasses should use
2598
 
        @needs_write_lock as this is a long running call its reasonable to
 
2638
        @needs_write_lock as this is a long running call it's reasonable to
2599
2639
        implicitly lock for the user.
2600
2640
 
2601
2641
        :param hint: If not supplied, the whole repository is packed.
2801
2841
            % (name, from_module),
2802
2842
            DeprecationWarning,
2803
2843
            stacklevel=2)
2804
 
        m = __import__(from_module, globals(), locals(), [name])
2805
2844
        try:
2806
 
            return getattr(m, name)
 
2845
            return pyutils.get_named_object(from_module, name)
2807
2846
        except AttributeError:
2808
2847
            raise AttributeError('module %s has no name %s'
2809
 
                    % (m, name))
 
2848
                    % (sys.modules[from_module], name))
2810
2849
    globals()[name] = _deprecated_repository_forwarder
2811
2850
 
2812
2851
for _name in [
3325
3364
    'bzrlib.repofmt.pack_repo',
3326
3365
    'RepositoryFormatKnitPack6RichRoot',
3327
3366
    )
 
3367
format_registry.register_lazy(
 
3368
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
3369
    'bzrlib.repofmt.groupcompress_repo',
 
3370
    'RepositoryFormat2a',
 
3371
    )
3328
3372
 
3329
3373
# Development formats.
3330
 
# Obsolete but kept pending a CHK based subtree format.
 
3374
# Check their docstrings to see if/when they are obsolete.
3331
3375
format_registry.register_lazy(
3332
3376
    ("Bazaar development format 2 with subtree support "
3333
3377
        "(needs bzr.dev from before 1.8)\n"),
3334
3378
    'bzrlib.repofmt.pack_repo',
3335
3379
    'RepositoryFormatPackDevelopment2Subtree',
3336
3380
    )
3337
 
 
3338
 
# 1.14->1.16 go below here
3339
 
format_registry.register_lazy(
3340
 
    'Bazaar development format - group compression and chk inventory'
3341
 
        ' (needs bzr.dev from 1.14)\n',
3342
 
    'bzrlib.repofmt.groupcompress_repo',
3343
 
    'RepositoryFormatCHK1',
3344
 
    )
3345
 
 
3346
 
format_registry.register_lazy(
3347
 
    'Bazaar development format - chk repository with bencode revision '
3348
 
        'serialization (needs bzr.dev from 1.16)\n',
3349
 
    'bzrlib.repofmt.groupcompress_repo',
3350
 
    'RepositoryFormatCHK2',
3351
 
    )
3352
 
format_registry.register_lazy(
3353
 
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
3354
 
    'bzrlib.repofmt.groupcompress_repo',
3355
 
    'RepositoryFormat2a',
 
3381
format_registry.register_lazy(
 
3382
    'Bazaar development format 8\n',
 
3383
    'bzrlib.repofmt.groupcompress_repo',
 
3384
    'RepositoryFormat2aSubtree',
3356
3385
    )
3357
3386
 
3358
3387
 
3466
3495
        return searcher.get_result()
3467
3496
 
3468
3497
    @needs_read_lock
3469
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
 
3498
    def search_missing_revision_ids(self,
 
3499
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
3500
            find_ghosts=True, revision_ids=None):
3470
3501
        """Return the revision ids that source has that target does not.
3471
3502
 
3472
3503
        :param revision_id: only return revision ids included by this
3473
3504
                            revision_id.
 
3505
        :param revision_ids: only return revision ids included by these
 
3506
                            revision_ids.
3474
3507
        :param find_ghosts: If True find missing revisions in deep history
3475
3508
            rather than just finding the surface difference.
3476
3509
        :return: A bzrlib.graph.SearchResult.
3477
3510
        """
 
3511
        if symbol_versioning.deprecated_passed(revision_id):
 
3512
            symbol_versioning.warn(
 
3513
                'search_missing_revision_ids(revision_id=...) was '
 
3514
                'deprecated in 2.3.  Use revision_ids=[...] instead.',
 
3515
                DeprecationWarning, stacklevel=2)
 
3516
            if revision_ids is not None:
 
3517
                raise AssertionError(
 
3518
                    'revision_ids is mutually exclusive with revision_id')
 
3519
            if revision_id is not None:
 
3520
                revision_ids = [revision_id]
 
3521
        del revision_id
 
3522
        mutter('search_missing_revision_ids locals: %r', locals())
3478
3523
        # stop searching at found target revisions.
3479
 
        if not find_ghosts and revision_id is not None:
3480
 
            return self._walk_to_common_revisions([revision_id])
 
3524
        if not find_ghosts and revision_ids is not None:
 
3525
            return self._walk_to_common_revisions(revision_ids)
3481
3526
        # generic, possibly worst case, slow code path.
3482
3527
        target_ids = set(self.target.all_revision_ids())
3483
 
        if revision_id is not None:
3484
 
            source_ids = self.source.get_ancestry(revision_id)
3485
 
            if source_ids[0] is not None:
3486
 
                raise AssertionError()
3487
 
            source_ids.pop(0)
3488
 
        else:
3489
 
            source_ids = self.source.all_revision_ids()
 
3528
        source_ids = self._present_source_revisions_for(revision_ids)
3490
3529
        result_set = set(source_ids).difference(target_ids)
3491
3530
        return self.source.revision_ids_to_search_result(result_set)
3492
3531
 
 
3532
    def _present_source_revisions_for(self, revision_ids):
 
3533
        """Returns set of all revisions in ancestry of revision_ids present in
 
3534
        the source repo.
 
3535
 
 
3536
        :param revision_ids: if None, all revisions in source are returned.
 
3537
        """
 
3538
        if revision_ids is not None:
 
3539
            # First, ensure all specified revisions exist.  Callers expect
 
3540
            # NoSuchRevision when they pass absent revision_ids here.
 
3541
            revision_ids = set(revision_ids)
 
3542
            graph = self.source.get_graph()
 
3543
            present_revs = set(graph.get_parent_map(revision_ids))
 
3544
            missing = revision_ids.difference(present_revs)
 
3545
            if missing:
 
3546
                raise errors.NoSuchRevision(self.source, missing.pop())
 
3547
            source_ids = [rev_id for (rev_id, parents) in
 
3548
                          self.source.get_graph().iter_ancestry(revision_ids)
 
3549
                          if rev_id != _mod_revision.NULL_REVISION
 
3550
                          and parents is not None]
 
3551
        else:
 
3552
            source_ids = self.source.all_revision_ids()
 
3553
        return set(source_ids)
 
3554
 
3493
3555
    @staticmethod
3494
3556
    def _same_model(source, target):
3495
3557
        """True if source and target have the same data representation.
3536
3598
        return InterRepository._same_model(source, target)
3537
3599
 
3538
3600
 
3539
 
class InterWeaveRepo(InterSameDataRepository):
3540
 
    """Optimised code paths between Weave based repositories.
3541
 
 
3542
 
    This should be in bzrlib/repofmt/weaverepo.py but we have not yet
3543
 
    implemented lazy inter-object optimisation.
3544
 
    """
3545
 
 
3546
 
    @classmethod
3547
 
    def _get_repo_format_to_test(self):
3548
 
        from bzrlib.repofmt import weaverepo
3549
 
        return weaverepo.RepositoryFormat7()
3550
 
 
3551
 
    @staticmethod
3552
 
    def is_compatible(source, target):
3553
 
        """Be compatible with known Weave formats.
3554
 
 
3555
 
        We don't test for the stores being of specific types because that
3556
 
        could lead to confusing results, and there is no need to be
3557
 
        overly general.
3558
 
        """
3559
 
        from bzrlib.repofmt.weaverepo import (
3560
 
                RepositoryFormat5,
3561
 
                RepositoryFormat6,
3562
 
                RepositoryFormat7,
3563
 
                )
3564
 
        try:
3565
 
            return (isinstance(source._format, (RepositoryFormat5,
3566
 
                                                RepositoryFormat6,
3567
 
                                                RepositoryFormat7)) and
3568
 
                    isinstance(target._format, (RepositoryFormat5,
3569
 
                                                RepositoryFormat6,
3570
 
                                                RepositoryFormat7)))
3571
 
        except AttributeError:
3572
 
            return False
3573
 
 
3574
 
    @needs_write_lock
3575
 
    def copy_content(self, revision_id=None):
3576
 
        """See InterRepository.copy_content()."""
3577
 
        # weave specific optimised path:
3578
 
        try:
3579
 
            self.target.set_make_working_trees(self.source.make_working_trees())
3580
 
        except (errors.RepositoryUpgradeRequired, NotImplemented):
3581
 
            pass
3582
 
        # FIXME do not peek!
3583
 
        if self.source._transport.listable():
3584
 
            pb = ui.ui_factory.nested_progress_bar()
3585
 
            try:
3586
 
                self.target.texts.insert_record_stream(
3587
 
                    self.source.texts.get_record_stream(
3588
 
                        self.source.texts.keys(), 'topological', False))
3589
 
                pb.update('Copying inventory', 0, 1)
3590
 
                self.target.inventories.insert_record_stream(
3591
 
                    self.source.inventories.get_record_stream(
3592
 
                        self.source.inventories.keys(), 'topological', False))
3593
 
                self.target.signatures.insert_record_stream(
3594
 
                    self.source.signatures.get_record_stream(
3595
 
                        self.source.signatures.keys(),
3596
 
                        'unordered', True))
3597
 
                self.target.revisions.insert_record_stream(
3598
 
                    self.source.revisions.get_record_stream(
3599
 
                        self.source.revisions.keys(),
3600
 
                        'topological', True))
3601
 
            finally:
3602
 
                pb.finished()
3603
 
        else:
3604
 
            self.target.fetch(self.source, revision_id=revision_id)
3605
 
 
3606
 
    @needs_read_lock
3607
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3608
 
        """See InterRepository.missing_revision_ids()."""
3609
 
        # we want all revisions to satisfy revision_id in source.
3610
 
        # but we don't want to stat every file here and there.
3611
 
        # we want then, all revisions other needs to satisfy revision_id
3612
 
        # checked, but not those that we have locally.
3613
 
        # so the first thing is to get a subset of the revisions to
3614
 
        # satisfy revision_id in source, and then eliminate those that
3615
 
        # we do already have.
3616
 
        # this is slow on high latency connection to self, but as this
3617
 
        # disk format scales terribly for push anyway due to rewriting
3618
 
        # inventory.weave, this is considered acceptable.
3619
 
        # - RBC 20060209
3620
 
        if revision_id is not None:
3621
 
            source_ids = self.source.get_ancestry(revision_id)
3622
 
            if source_ids[0] is not None:
3623
 
                raise AssertionError()
3624
 
            source_ids.pop(0)
3625
 
        else:
3626
 
            source_ids = self.source._all_possible_ids()
3627
 
        source_ids_set = set(source_ids)
3628
 
        # source_ids is the worst possible case we may need to pull.
3629
 
        # now we want to filter source_ids against what we actually
3630
 
        # have in target, but don't try to check for existence where we know
3631
 
        # we do not have a revision as that would be pointless.
3632
 
        target_ids = set(self.target._all_possible_ids())
3633
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3634
 
        actually_present_revisions = set(
3635
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3636
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3637
 
        if revision_id is not None:
3638
 
            # we used get_ancestry to determine source_ids then we are assured all
3639
 
            # revisions referenced are present as they are installed in topological order.
3640
 
            # and the tip revision was validated by get_ancestry.
3641
 
            result_set = required_revisions
3642
 
        else:
3643
 
            # if we just grabbed the possibly available ids, then
3644
 
            # we only have an estimate of whats available and need to validate
3645
 
            # that against the revision records.
3646
 
            result_set = set(
3647
 
                self.source._eliminate_revisions_not_present(required_revisions))
3648
 
        return self.source.revision_ids_to_search_result(result_set)
3649
 
 
3650
 
 
3651
 
class InterKnitRepo(InterSameDataRepository):
3652
 
    """Optimised code paths between Knit based repositories."""
3653
 
 
3654
 
    @classmethod
3655
 
    def _get_repo_format_to_test(self):
3656
 
        from bzrlib.repofmt import knitrepo
3657
 
        return knitrepo.RepositoryFormatKnit1()
3658
 
 
3659
 
    @staticmethod
3660
 
    def is_compatible(source, target):
3661
 
        """Be compatible with known Knit formats.
3662
 
 
3663
 
        We don't test for the stores being of specific types because that
3664
 
        could lead to confusing results, and there is no need to be
3665
 
        overly general.
3666
 
        """
3667
 
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
3668
 
        try:
3669
 
            are_knits = (isinstance(source._format, RepositoryFormatKnit) and
3670
 
                isinstance(target._format, RepositoryFormatKnit))
3671
 
        except AttributeError:
3672
 
            return False
3673
 
        return are_knits and InterRepository._same_model(source, target)
3674
 
 
3675
 
    @needs_read_lock
3676
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3677
 
        """See InterRepository.missing_revision_ids()."""
3678
 
        if revision_id is not None:
3679
 
            source_ids = self.source.get_ancestry(revision_id)
3680
 
            if source_ids[0] is not None:
3681
 
                raise AssertionError()
3682
 
            source_ids.pop(0)
3683
 
        else:
3684
 
            source_ids = self.source.all_revision_ids()
3685
 
        source_ids_set = set(source_ids)
3686
 
        # source_ids is the worst possible case we may need to pull.
3687
 
        # now we want to filter source_ids against what we actually
3688
 
        # have in target, but don't try to check for existence where we know
3689
 
        # we do not have a revision as that would be pointless.
3690
 
        target_ids = set(self.target.all_revision_ids())
3691
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3692
 
        actually_present_revisions = set(
3693
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3694
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3695
 
        if revision_id is not None:
3696
 
            # we used get_ancestry to determine source_ids then we are assured all
3697
 
            # revisions referenced are present as they are installed in topological order.
3698
 
            # and the tip revision was validated by get_ancestry.
3699
 
            result_set = required_revisions
3700
 
        else:
3701
 
            # if we just grabbed the possibly available ids, then
3702
 
            # we only have an estimate of whats available and need to validate
3703
 
            # that against the revision records.
3704
 
            result_set = set(
3705
 
                self.source._eliminate_revisions_not_present(required_revisions))
3706
 
        return self.source.revision_ids_to_search_result(result_set)
3707
 
 
3708
 
 
3709
3601
class InterDifferingSerializer(InterRepository):
3710
3602
 
3711
3603
    @classmethod
3813
3705
                basis_id, delta, current_revision_id, parents_parents)
3814
3706
            cache[current_revision_id] = parent_tree
3815
3707
 
3816
 
    def _fetch_batch(self, revision_ids, basis_id, cache, a_graph=None):
 
3708
    def _fetch_batch(self, revision_ids, basis_id, cache):
3817
3709
        """Fetch across a few revisions.
3818
3710
 
3819
3711
        :param revision_ids: The revisions to copy
3820
3712
        :param basis_id: The revision_id of a tree that must be in cache, used
3821
3713
            as a basis for delta when no other base is available
3822
3714
        :param cache: A cache of RevisionTrees that we can use.
3823
 
        :param a_graph: A Graph object to determine the heads() of the
3824
 
            rich-root data stream.
3825
3715
        :return: The revision_id of the last converted tree. The RevisionTree
3826
3716
            for it will be in cache
3827
3717
        """
3895
3785
        if root_keys_to_create:
3896
3786
            root_stream = _mod_fetch._new_root_data_stream(
3897
3787
                root_keys_to_create, self._revision_id_to_root_id, parent_map,
3898
 
                self.source, graph=a_graph)
 
3788
                self.source)
3899
3789
            to_texts.insert_record_stream(root_stream)
3900
3790
        to_texts.insert_record_stream(from_texts.get_record_stream(
3901
3791
            text_keys, self.target._format._fetch_order,
3958
3848
        cache[basis_id] = basis_tree
3959
3849
        del basis_tree # We don't want to hang on to it here
3960
3850
        hints = []
3961
 
        if self._converting_to_rich_root and len(revision_ids) > 100:
3962
 
            a_graph = _mod_fetch._get_rich_root_heads_graph(self.source,
3963
 
                                                            revision_ids)
3964
 
        else:
3965
 
            a_graph = None
 
3851
        a_graph = None
3966
3852
 
3967
3853
        for offset in range(0, len(revision_ids), batch_size):
3968
3854
            self.target.start_write_group()
3970
3856
                pb.update('Transferring revisions', offset,
3971
3857
                          len(revision_ids))
3972
3858
                batch = revision_ids[offset:offset+batch_size]
3973
 
                basis_id = self._fetch_batch(batch, basis_id, cache,
3974
 
                                             a_graph=a_graph)
 
3859
                basis_id = self._fetch_batch(batch, basis_id, cache)
3975
3860
            except:
3976
3861
                self.source._safe_to_return_from_cache = False
3977
3862
                self.target.abort_write_group()
4003
3888
            ui.ui_factory.show_user_warning('cross_format_fetch',
4004
3889
                from_format=self.source._format,
4005
3890
                to_format=self.target._format)
 
3891
        if revision_id:
 
3892
            search_revision_ids = [revision_id]
 
3893
        else:
 
3894
            search_revision_ids = None
4006
3895
        revision_ids = self.target.search_missing_revision_ids(self.source,
4007
 
            revision_id, find_ghosts=find_ghosts).get_keys()
 
3896
            revision_ids=search_revision_ids, find_ghosts=find_ghosts).get_keys()
4008
3897
        if not revision_ids:
4009
3898
            return 0, 0
4010
3899
        revision_ids = tsort.topo_sort(
4043
3932
            basis_id = first_rev.parent_ids[0]
4044
3933
            # only valid as a basis if the target has it
4045
3934
            self.target.get_revision(basis_id)
4046
 
            # Try to get a basis tree - if its a ghost it will hit the
 
3935
            # Try to get a basis tree - if it's a ghost it will hit the
4047
3936
            # NoSuchRevision case.
4048
3937
            basis_tree = self.source.revision_tree(basis_id)
4049
3938
        except (IndexError, errors.NoSuchRevision):
4054
3943
 
4055
3944
InterRepository.register_optimiser(InterDifferingSerializer)
4056
3945
InterRepository.register_optimiser(InterSameDataRepository)
4057
 
InterRepository.register_optimiser(InterWeaveRepo)
4058
 
InterRepository.register_optimiser(InterKnitRepo)
4059
3946
 
4060
3947
 
4061
3948
class CopyConverter(object):
4249
4136
                is_resume = False
4250
4137
            try:
4251
4138
                # locked_insert_stream performs a commit|suspend.
4252
 
                return self._locked_insert_stream(stream, src_format, is_resume)
 
4139
                return self._locked_insert_stream(stream, src_format,
 
4140
                    is_resume)
4253
4141
            except:
4254
4142
                self.target_repo.abort_write_group(suppress_errors=True)
4255
4143
                raise
4302
4190
                # required if the serializers are different only in terms of
4303
4191
                # the inventory.
4304
4192
                if src_serializer == to_serializer:
4305
 
                    self.target_repo.revisions.insert_record_stream(
4306
 
                        substream)
 
4193
                    self.target_repo.revisions.insert_record_stream(substream)
4307
4194
                else:
4308
4195
                    self._extract_and_insert_revisions(substream,
4309
4196
                        src_serializer)
4417
4304
        """Create a StreamSource streaming from from_repository."""
4418
4305
        self.from_repository = from_repository
4419
4306
        self.to_format = to_format
 
4307
        self._record_counter = RecordCounter()
4420
4308
 
4421
4309
    def delta_on_metadata(self):
4422
4310
        """Return True if delta's are permitted on metadata streams.