~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

  • Committer: Jelmer Vernooij
  • Date: 2010-12-20 11:57:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5577.
  • Revision ID: jelmer@samba.org-20101220115714-2ru3hfappjweeg7q
Don't use no-plugins.

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,
 
44
    static_tuple,
43
45
    symbol_versioning,
44
46
    trace,
45
47
    tsort,
46
 
    ui,
47
48
    versionedfile,
48
49
    )
49
50
from bzrlib.bundle import serializer
52
53
from bzrlib.testament import Testament
53
54
""")
54
55
 
 
56
import sys
 
57
from bzrlib import (
 
58
    errors,
 
59
    registry,
 
60
    ui,
 
61
    )
55
62
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
56
63
from bzrlib.inter import InterObject
57
64
from bzrlib.inventory import (
60
67
    ROOT_ID,
61
68
    entry_factory,
62
69
    )
63
 
from bzrlib.lock import _RelockDebugMixin
64
 
from bzrlib import registry
 
70
from bzrlib.recordcounter import RecordCounter
 
71
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
65
72
from bzrlib.trace import (
66
73
    log_exception_quietly, note, mutter, mutter_callsite, warning)
67
74
 
70
77
_deprecation_warning_done = False
71
78
 
72
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
 
73
88
class CommitBuilder(object):
74
89
    """Provides an interface to build up a commit.
75
90
 
100
115
 
101
116
        if committer is None:
102
117
            self._committer = self._config.username()
 
118
        elif not isinstance(committer, unicode):
 
119
            self._committer = committer.decode() # throw if non-ascii
103
120
        else:
104
121
            self._committer = committer
105
122
 
230
247
 
231
248
    def _gen_revision_id(self):
232
249
        """Return new revision-id."""
233
 
        return generate_ids.gen_revision_id(self._config.username(),
234
 
                                            self._timestamp)
 
250
        return generate_ids.gen_revision_id(self._committer, self._timestamp)
235
251
 
236
252
    def _generate_revision_if_needed(self):
237
253
        """Create a revision id if None was supplied.
277
293
 
278
294
        :param tree: The tree which is being committed.
279
295
        """
280
 
        # NB: if there are no parents then this method is not called, so no
281
 
        # need to guard on parents having length.
 
296
        if len(self.parents) == 0:
 
297
            raise errors.RootMissing()
282
298
        entry = entry_factory['directory'](tree.path2id(''), '',
283
299
            None)
284
300
        entry.revision = self._new_revision_id
422
438
            else:
423
439
                # we don't need to commit this, because the caller already
424
440
                # determined that an existing revision of this file is
425
 
                # appropriate. If its not being considered for committing then
 
441
                # appropriate. If it's not being considered for committing then
426
442
                # it and all its parents to the root must be unaltered so
427
443
                # no-change against the basis.
428
444
                if ie.revision == self._new_revision_id:
744
760
                    # after iter_changes examines and decides it has changed,
745
761
                    # we will unconditionally record a new version even if some
746
762
                    # other process reverts it while commit is running (with
747
 
                    # the revert happening after iter_changes did it's
 
763
                    # the revert happening after iter_changes did its
748
764
                    # examination).
749
765
                    if change[7][1]:
750
766
                        entry.executable = True
859
875
        # versioned roots do not change unless the tree found a change.
860
876
 
861
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
 
862
895
######################################################################
863
896
# Repositories
864
897
 
865
898
 
866
 
class Repository(_RelockDebugMixin):
 
899
class Repository(_RelockDebugMixin, controldir.ControlComponent):
867
900
    """Repository holding history for one or more branches.
868
901
 
869
902
    The repository holds and retrieves historical information including
916
949
        pointing to .bzr/repository.
917
950
    """
918
951
 
919
 
    # 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
920
953
    # in a Repository class subclass rather than to override
921
954
    # get_commit_builder.
922
955
    _commit_builder_class = CommitBuilder
1017
1050
                " id and insertion revid (%r, %r)"
1018
1051
                % (inv.revision_id, revision_id))
1019
1052
        if inv.root is None:
1020
 
            raise AssertionError()
 
1053
            raise errors.RootMissing()
1021
1054
        return self._add_inventory_checked(revision_id, inv, parents)
1022
1055
 
1023
1056
    def _add_inventory_checked(self, revision_id, inv, parents):
1027
1060
 
1028
1061
        :seealso: add_inventory, for the contract.
1029
1062
        """
1030
 
        inv_lines = self._serialise_inventory_to_lines(inv)
 
1063
        inv_lines = self._serializer.write_inventory_to_lines(inv)
1031
1064
        return self._inventory_add_lines(revision_id, parents,
1032
1065
            inv_lines, check_content=False)
1033
1066
 
1290
1323
 
1291
1324
        :param _format: The format of the repository on disk.
1292
1325
        :param a_bzrdir: The BzrDir of the repository.
1293
 
 
1294
 
        In the future we will have a single api for all stores for
1295
 
        getting file texts, inventories and revisions, then
1296
 
        this construct will accept instances of those things.
1297
1326
        """
 
1327
        # In the future we will have a single api for all stores for
 
1328
        # getting file texts, inventories and revisions, then
 
1329
        # this construct will accept instances of those things.
1298
1330
        super(Repository, self).__init__()
1299
1331
        self._format = _format
1300
1332
        # the following are part of the public API for Repository:
1315
1347
        # rather copying them?
1316
1348
        self._safe_to_return_from_cache = False
1317
1349
 
 
1350
    @property
 
1351
    def user_transport(self):
 
1352
        return self.bzrdir.user_transport
 
1353
 
 
1354
    @property
 
1355
    def control_transport(self):
 
1356
        return self._transport
 
1357
 
1318
1358
    def __repr__(self):
1319
1359
        if self._fallback_repositories:
1320
1360
            return '%s(%r, fallback_repositories=%r)' % (
1368
1408
        data during reads, and allows a 'write_group' to be obtained. Write
1369
1409
        groups must be used for actual data insertion.
1370
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
 
1371
1417
        :param token: if this is already locked, then lock_write will fail
1372
1418
            unless the token matches the existing lock.
1373
1419
        :returns: a token if this instance supports tokens, otherwise None.
1376
1422
        :raises MismatchedToken: if the specified token doesn't match the token
1377
1423
            of the existing lock.
1378
1424
        :seealso: start_write_group.
1379
 
 
1380
 
        A token should be passed in if you know that you have locked the object
1381
 
        some other way, and need to synchronise this object's state with that
1382
 
        fact.
1383
 
 
1384
 
        XXX: this docstring is duplicated in many places, e.g. lockable_files.py
 
1425
        :return: A RepositoryWriteLockResult.
1385
1426
        """
1386
1427
        locked = self.is_locked()
1387
 
        result = self.control_files.lock_write(token=token)
 
1428
        token = self.control_files.lock_write(token=token)
1388
1429
        if not locked:
1389
1430
            self._warn_if_deprecated()
1390
1431
            self._note_lock('w')
1392
1433
                # Writes don't affect fallback repos
1393
1434
                repo.lock_read()
1394
1435
            self._refresh_data()
1395
 
        return result
 
1436
        return RepositoryWriteLockResult(self.unlock, token)
1396
1437
 
1397
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
        """
1398
1444
        locked = self.is_locked()
1399
1445
        self.control_files.lock_read()
1400
1446
        if not locked:
1403
1449
            for repo in self._fallback_repositories:
1404
1450
                repo.lock_read()
1405
1451
            self._refresh_data()
 
1452
        return LogicalLockResult(self.unlock)
1406
1453
 
1407
1454
    def get_physical_lock_status(self):
1408
1455
        return self.control_files.get_physical_lock_status()
1468
1515
 
1469
1516
        # now gather global repository information
1470
1517
        # XXX: This is available for many repos regardless of listability.
1471
 
        if self.bzrdir.root_transport.listable():
 
1518
        if self.user_transport.listable():
1472
1519
            # XXX: do we want to __define len__() ?
1473
1520
            # Maybe the versionedfiles object should provide a different
1474
1521
            # method to get the number of keys.
1506
1553
 
1507
1554
        ret = []
1508
1555
        for branches, repository in bzrdir.BzrDir.find_bzrdirs(
1509
 
                self.bzrdir.root_transport, evaluate=Evaluator()):
 
1556
                self.user_transport, evaluate=Evaluator()):
1510
1557
            if branches is not None:
1511
1558
                ret.extend(branches)
1512
1559
            if not using and repository is not None:
1626
1673
        return missing_keys
1627
1674
 
1628
1675
    def refresh_data(self):
1629
 
        """Re-read any data needed to to synchronise with disk.
 
1676
        """Re-read any data needed to synchronise with disk.
1630
1677
 
1631
1678
        This method is intended to be called after another repository instance
1632
1679
        (such as one used by a smart server) has inserted data into the
1633
 
        repository. It may not be called during a write group, but may be
1634
 
        called at any other time.
 
1680
        repository. On all repositories this will work outside of write groups.
 
1681
        Some repository formats (pack and newer for bzrlib native formats)
 
1682
        support refresh_data inside write groups. If called inside a write
 
1683
        group on a repository that does not support refreshing in a write group
 
1684
        IsInWriteGroupError will be raised.
1635
1685
        """
1636
 
        if self.is_in_write_group():
1637
 
            raise errors.InternalBzrError(
1638
 
                "May not refresh_data while in a write group.")
1639
1686
        self._refresh_data()
1640
1687
 
1641
1688
    def resume_write_group(self, tokens):
1680
1727
                "May not fetch while in a write group.")
1681
1728
        # fast path same-url fetch operations
1682
1729
        # TODO: lift out to somewhere common with RemoteRepository
1683
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401646>
 
1730
        # <https://bugs.launchpad.net/bzr/+bug/401646>
1684
1731
        if (self.has_same_location(source)
1685
1732
            and fetch_spec is None
1686
1733
            and self._has_same_fallbacks(source)):
1895
1942
                rev = self._serializer.read_revision_from_string(text)
1896
1943
                yield (revid, rev)
1897
1944
 
1898
 
    @needs_read_lock
1899
 
    def get_revision_xml(self, revision_id):
1900
 
        # TODO: jam 20070210 This shouldn't be necessary since get_revision
1901
 
        #       would have already do it.
1902
 
        # TODO: jam 20070210 Just use _serializer.write_revision_to_string()
1903
 
        # TODO: this can't just be replaced by:
1904
 
        # return self._serializer.write_revision_to_string(
1905
 
        #     self.get_revision(revision_id))
1906
 
        # as cStringIO preservers the encoding unlike write_revision_to_string
1907
 
        # or some other call down the path.
1908
 
        rev = self.get_revision(revision_id)
1909
 
        rev_tmp = cStringIO.StringIO()
1910
 
        # the current serializer..
1911
 
        self._serializer.write_revision(rev, rev_tmp)
1912
 
        rev_tmp.seek(0)
1913
 
        return rev_tmp.getvalue()
1914
 
 
1915
1945
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1916
1946
        """Produce a generator of revision deltas.
1917
1947
 
2430
2460
                result.revision_id, revision_id))
2431
2461
        return result
2432
2462
 
2433
 
    def _serialise_inventory(self, inv):
2434
 
        return self._serializer.write_inventory_to_string(inv)
2435
 
 
2436
 
    def _serialise_inventory_to_lines(self, inv):
2437
 
        return self._serializer.write_inventory_to_lines(inv)
2438
 
 
2439
2463
    def get_serializer_format(self):
2440
2464
        return self._serializer.format_num
2441
2465
 
2491
2515
            ancestors will be traversed.
2492
2516
        """
2493
2517
        graph = self.get_graph()
2494
 
        next_id = revision_id
2495
 
        while True:
2496
 
            if next_id in (None, _mod_revision.NULL_REVISION):
2497
 
                return
2498
 
            try:
2499
 
                parents = graph.get_parent_map([next_id])[next_id]
2500
 
            except KeyError:
2501
 
                raise errors.RevisionNotPresent(next_id, self)
2502
 
            yield next_id
2503
 
            if len(parents) == 0:
2504
 
                return
2505
 
            else:
2506
 
                next_id = parents[0]
2507
 
 
2508
 
    @needs_read_lock
2509
 
    def get_revision_inventory(self, revision_id):
2510
 
        """Return inventory of a past revision."""
2511
 
        # TODO: Unify this with get_inventory()
2512
 
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
2513
 
        # must be the same as its revision, so this is trivial.
2514
 
        if revision_id is None:
2515
 
            # This does not make sense: if there is no revision,
2516
 
            # then it is the current tree inventory surely ?!
2517
 
            # and thus get_root_id() is something that looks at the last
2518
 
            # commit on the branch, and the get_root_id is an inventory check.
2519
 
            raise NotImplementedError
2520
 
            # return Inventory(self.get_root_id())
2521
 
        else:
2522
 
            return self.get_inventory(revision_id)
 
2518
        stop_revisions = (None, _mod_revision.NULL_REVISION)
 
2519
        return graph.iter_lefthand_ancestry(revision_id, stop_revisions)
2523
2520
 
2524
2521
    def is_shared(self):
2525
2522
        """Return True if this repository is flagged as a shared repository."""
2560
2557
            return RevisionTree(self, Inventory(root_id=None),
2561
2558
                                _mod_revision.NULL_REVISION)
2562
2559
        else:
2563
 
            inv = self.get_revision_inventory(revision_id)
 
2560
            inv = self.get_inventory(revision_id)
2564
2561
            return RevisionTree(self, inv, revision_id)
2565
2562
 
2566
2563
    def revision_trees(self, revision_ids):
2619
2616
            keys = tsort.topo_sort(parent_map)
2620
2617
        return [None] + list(keys)
2621
2618
 
2622
 
    def pack(self, hint=None):
 
2619
    def pack(self, hint=None, clean_obsolete_packs=False):
2623
2620
        """Compress the data within the repository.
2624
2621
 
2625
2622
        This operation only makes sense for some repository types. For other
2626
2623
        types it should be a no-op that just returns.
2627
2624
 
2628
2625
        This stub method does not require a lock, but subclasses should use
2629
 
        @needs_write_lock as this is a long running call its reasonable to
 
2626
        @needs_write_lock as this is a long running call it's reasonable to
2630
2627
        implicitly lock for the user.
2631
2628
 
2632
2629
        :param hint: If not supplied, the whole repository is packed.
2635
2632
            obtained from the result of commit_write_group(). Out of
2636
2633
            date hints are simply ignored, because concurrent operations
2637
2634
            can obsolete them rapidly.
 
2635
 
 
2636
        :param clean_obsolete_packs: Clean obsolete packs immediately after
 
2637
            the pack operation.
2638
2638
        """
2639
2639
 
2640
2640
    def get_transaction(self):
2665
2665
    def _make_parents_provider(self):
2666
2666
        return self
2667
2667
 
 
2668
    @needs_read_lock
 
2669
    def get_known_graph_ancestry(self, revision_ids):
 
2670
        """Return the known graph for a set of revision ids and their ancestors.
 
2671
        """
 
2672
        st = static_tuple.StaticTuple
 
2673
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
 
2674
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
 
2675
        return graph.GraphThunkIdsToKeys(known_graph)
 
2676
 
2668
2677
    def get_graph(self, other_repository=None):
2669
2678
        """Return the graph walker for this repository format"""
2670
2679
        parents_provider = self._make_parents_provider()
2820
2829
            % (name, from_module),
2821
2830
            DeprecationWarning,
2822
2831
            stacklevel=2)
2823
 
        m = __import__(from_module, globals(), locals(), [name])
2824
2832
        try:
2825
 
            return getattr(m, name)
 
2833
            return pyutils.get_named_object(from_module, name)
2826
2834
        except AttributeError:
2827
2835
            raise AttributeError('module %s has no name %s'
2828
 
                    % (m, name))
 
2836
                    % (sys.modules[from_module], name))
2829
2837
    globals()[name] = _deprecated_repository_forwarder
2830
2838
 
2831
2839
for _name in [
3072
3080
    # Is the format experimental ?
3073
3081
    experimental = False
3074
3082
 
3075
 
    def __str__(self):
3076
 
        return "<%s>" % self.__class__.__name__
 
3083
    def __repr__(self):
 
3084
        return "%s()" % self.__class__.__name__
3077
3085
 
3078
3086
    def __eq__(self, other):
3079
3087
        # format objects are generally stateless
3197
3205
        """
3198
3206
        raise NotImplementedError(self.open)
3199
3207
 
 
3208
    def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
 
3209
        from bzrlib.bzrdir import BzrDir, RepoInitHookParams
 
3210
        hooks = BzrDir.hooks['post_repo_init']
 
3211
        if not hooks:
 
3212
            return
 
3213
        params = RepoInitHookParams(repository, self, a_bzrdir, shared)
 
3214
        for hook in hooks:
 
3215
            hook(params)
 
3216
 
3200
3217
 
3201
3218
class MetaDirRepositoryFormat(RepositoryFormat):
3202
3219
    """Common base class for the new repositories using the metadir layout."""
3335
3352
    'bzrlib.repofmt.pack_repo',
3336
3353
    'RepositoryFormatKnitPack6RichRoot',
3337
3354
    )
 
3355
format_registry.register_lazy(
 
3356
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
 
3357
    'bzrlib.repofmt.groupcompress_repo',
 
3358
    'RepositoryFormat2a',
 
3359
    )
3338
3360
 
3339
3361
# Development formats.
3340
 
# Obsolete but kept pending a CHK based subtree format.
 
3362
# Check their docstrings to see if/when they are obsolete.
3341
3363
format_registry.register_lazy(
3342
3364
    ("Bazaar development format 2 with subtree support "
3343
3365
        "(needs bzr.dev from before 1.8)\n"),
3344
3366
    'bzrlib.repofmt.pack_repo',
3345
3367
    'RepositoryFormatPackDevelopment2Subtree',
3346
3368
    )
3347
 
 
3348
 
# 1.14->1.16 go below here
3349
 
format_registry.register_lazy(
3350
 
    'Bazaar development format - group compression and chk inventory'
3351
 
        ' (needs bzr.dev from 1.14)\n',
3352
 
    'bzrlib.repofmt.groupcompress_repo',
3353
 
    'RepositoryFormatCHK1',
3354
 
    )
3355
 
 
3356
 
format_registry.register_lazy(
3357
 
    'Bazaar development format - chk repository with bencode revision '
3358
 
        'serialization (needs bzr.dev from 1.16)\n',
3359
 
    'bzrlib.repofmt.groupcompress_repo',
3360
 
    'RepositoryFormatCHK2',
3361
 
    )
3362
 
format_registry.register_lazy(
3363
 
    'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
3364
 
    'bzrlib.repofmt.groupcompress_repo',
3365
 
    'RepositoryFormat2a',
 
3369
format_registry.register_lazy(
 
3370
    'Bazaar development format 8\n',
 
3371
    'bzrlib.repofmt.groupcompress_repo',
 
3372
    'RepositoryFormat2aSubtree',
3366
3373
    )
3367
3374
 
3368
3375
 
3411
3418
        :return: None.
3412
3419
        """
3413
3420
        ui.ui_factory.warn_experimental_format_fetch(self)
3414
 
        f = _mod_fetch.RepoFetcher(to_repository=self.target,
 
3421
        from bzrlib.fetch import RepoFetcher
 
3422
        # See <https://launchpad.net/bugs/456077> asking for a warning here
 
3423
        if self.source._format.network_name() != self.target._format.network_name():
 
3424
            ui.ui_factory.show_user_warning('cross_format_fetch',
 
3425
                from_format=self.source._format,
 
3426
                to_format=self.target._format)
 
3427
        f = RepoFetcher(to_repository=self.target,
3415
3428
                               from_repository=self.source,
3416
3429
                               last_revision=revision_id,
3417
3430
                               fetch_spec=fetch_spec,
3540
3553
        return InterRepository._same_model(source, target)
3541
3554
 
3542
3555
 
3543
 
class InterWeaveRepo(InterSameDataRepository):
3544
 
    """Optimised code paths between Weave based repositories.
3545
 
 
3546
 
    This should be in bzrlib/repofmt/weaverepo.py but we have not yet
3547
 
    implemented lazy inter-object optimisation.
3548
 
    """
3549
 
 
3550
 
    @classmethod
3551
 
    def _get_repo_format_to_test(self):
3552
 
        from bzrlib.repofmt import weaverepo
3553
 
        return weaverepo.RepositoryFormat7()
3554
 
 
3555
 
    @staticmethod
3556
 
    def is_compatible(source, target):
3557
 
        """Be compatible with known Weave formats.
3558
 
 
3559
 
        We don't test for the stores being of specific types because that
3560
 
        could lead to confusing results, and there is no need to be
3561
 
        overly general.
3562
 
        """
3563
 
        from bzrlib.repofmt.weaverepo import (
3564
 
                RepositoryFormat5,
3565
 
                RepositoryFormat6,
3566
 
                RepositoryFormat7,
3567
 
                )
3568
 
        try:
3569
 
            return (isinstance(source._format, (RepositoryFormat5,
3570
 
                                                RepositoryFormat6,
3571
 
                                                RepositoryFormat7)) and
3572
 
                    isinstance(target._format, (RepositoryFormat5,
3573
 
                                                RepositoryFormat6,
3574
 
                                                RepositoryFormat7)))
3575
 
        except AttributeError:
3576
 
            return False
3577
 
 
3578
 
    @needs_write_lock
3579
 
    def copy_content(self, revision_id=None):
3580
 
        """See InterRepository.copy_content()."""
3581
 
        # weave specific optimised path:
3582
 
        try:
3583
 
            self.target.set_make_working_trees(self.source.make_working_trees())
3584
 
        except (errors.RepositoryUpgradeRequired, NotImplemented):
3585
 
            pass
3586
 
        # FIXME do not peek!
3587
 
        if self.source._transport.listable():
3588
 
            pb = ui.ui_factory.nested_progress_bar()
3589
 
            try:
3590
 
                self.target.texts.insert_record_stream(
3591
 
                    self.source.texts.get_record_stream(
3592
 
                        self.source.texts.keys(), 'topological', False))
3593
 
                pb.update('Copying inventory', 0, 1)
3594
 
                self.target.inventories.insert_record_stream(
3595
 
                    self.source.inventories.get_record_stream(
3596
 
                        self.source.inventories.keys(), 'topological', False))
3597
 
                self.target.signatures.insert_record_stream(
3598
 
                    self.source.signatures.get_record_stream(
3599
 
                        self.source.signatures.keys(),
3600
 
                        'unordered', True))
3601
 
                self.target.revisions.insert_record_stream(
3602
 
                    self.source.revisions.get_record_stream(
3603
 
                        self.source.revisions.keys(),
3604
 
                        'topological', True))
3605
 
            finally:
3606
 
                pb.finished()
3607
 
        else:
3608
 
            self.target.fetch(self.source, revision_id=revision_id)
3609
 
 
3610
 
    @needs_read_lock
3611
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3612
 
        """See InterRepository.missing_revision_ids()."""
3613
 
        # we want all revisions to satisfy revision_id in source.
3614
 
        # but we don't want to stat every file here and there.
3615
 
        # we want then, all revisions other needs to satisfy revision_id
3616
 
        # checked, but not those that we have locally.
3617
 
        # so the first thing is to get a subset of the revisions to
3618
 
        # satisfy revision_id in source, and then eliminate those that
3619
 
        # we do already have.
3620
 
        # this is slow on high latency connection to self, but as this
3621
 
        # disk format scales terribly for push anyway due to rewriting
3622
 
        # inventory.weave, this is considered acceptable.
3623
 
        # - RBC 20060209
3624
 
        if revision_id is not None:
3625
 
            source_ids = self.source.get_ancestry(revision_id)
3626
 
            if source_ids[0] is not None:
3627
 
                raise AssertionError()
3628
 
            source_ids.pop(0)
3629
 
        else:
3630
 
            source_ids = self.source._all_possible_ids()
3631
 
        source_ids_set = set(source_ids)
3632
 
        # source_ids is the worst possible case we may need to pull.
3633
 
        # now we want to filter source_ids against what we actually
3634
 
        # have in target, but don't try to check for existence where we know
3635
 
        # we do not have a revision as that would be pointless.
3636
 
        target_ids = set(self.target._all_possible_ids())
3637
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3638
 
        actually_present_revisions = set(
3639
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3640
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3641
 
        if revision_id is not None:
3642
 
            # we used get_ancestry to determine source_ids then we are assured all
3643
 
            # revisions referenced are present as they are installed in topological order.
3644
 
            # and the tip revision was validated by get_ancestry.
3645
 
            result_set = required_revisions
3646
 
        else:
3647
 
            # if we just grabbed the possibly available ids, then
3648
 
            # we only have an estimate of whats available and need to validate
3649
 
            # that against the revision records.
3650
 
            result_set = set(
3651
 
                self.source._eliminate_revisions_not_present(required_revisions))
3652
 
        return self.source.revision_ids_to_search_result(result_set)
3653
 
 
3654
 
 
3655
 
class InterKnitRepo(InterSameDataRepository):
3656
 
    """Optimised code paths between Knit based repositories."""
3657
 
 
3658
 
    @classmethod
3659
 
    def _get_repo_format_to_test(self):
3660
 
        from bzrlib.repofmt import knitrepo
3661
 
        return knitrepo.RepositoryFormatKnit1()
3662
 
 
3663
 
    @staticmethod
3664
 
    def is_compatible(source, target):
3665
 
        """Be compatible with known Knit formats.
3666
 
 
3667
 
        We don't test for the stores being of specific types because that
3668
 
        could lead to confusing results, and there is no need to be
3669
 
        overly general.
3670
 
        """
3671
 
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
3672
 
        try:
3673
 
            are_knits = (isinstance(source._format, RepositoryFormatKnit) and
3674
 
                isinstance(target._format, RepositoryFormatKnit))
3675
 
        except AttributeError:
3676
 
            return False
3677
 
        return are_knits and InterRepository._same_model(source, target)
3678
 
 
3679
 
    @needs_read_lock
3680
 
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3681
 
        """See InterRepository.missing_revision_ids()."""
3682
 
        if revision_id is not None:
3683
 
            source_ids = self.source.get_ancestry(revision_id)
3684
 
            if source_ids[0] is not None:
3685
 
                raise AssertionError()
3686
 
            source_ids.pop(0)
3687
 
        else:
3688
 
            source_ids = self.source.all_revision_ids()
3689
 
        source_ids_set = set(source_ids)
3690
 
        # source_ids is the worst possible case we may need to pull.
3691
 
        # now we want to filter source_ids against what we actually
3692
 
        # have in target, but don't try to check for existence where we know
3693
 
        # we do not have a revision as that would be pointless.
3694
 
        target_ids = set(self.target.all_revision_ids())
3695
 
        possibly_present_revisions = target_ids.intersection(source_ids_set)
3696
 
        actually_present_revisions = set(
3697
 
            self.target._eliminate_revisions_not_present(possibly_present_revisions))
3698
 
        required_revisions = source_ids_set.difference(actually_present_revisions)
3699
 
        if revision_id is not None:
3700
 
            # we used get_ancestry to determine source_ids then we are assured all
3701
 
            # revisions referenced are present as they are installed in topological order.
3702
 
            # and the tip revision was validated by get_ancestry.
3703
 
            result_set = required_revisions
3704
 
        else:
3705
 
            # if we just grabbed the possibly available ids, then
3706
 
            # we only have an estimate of whats available and need to validate
3707
 
            # that against the revision records.
3708
 
            result_set = set(
3709
 
                self.source._eliminate_revisions_not_present(required_revisions))
3710
 
        return self.source.revision_ids_to_search_result(result_set)
3711
 
 
3712
 
 
3713
3556
class InterDifferingSerializer(InterRepository):
3714
3557
 
3715
3558
    @classmethod
3817
3660
                basis_id, delta, current_revision_id, parents_parents)
3818
3661
            cache[current_revision_id] = parent_tree
3819
3662
 
3820
 
    def _fetch_batch(self, revision_ids, basis_id, cache, a_graph=None):
 
3663
    def _fetch_batch(self, revision_ids, basis_id, cache):
3821
3664
        """Fetch across a few revisions.
3822
3665
 
3823
3666
        :param revision_ids: The revisions to copy
3824
3667
        :param basis_id: The revision_id of a tree that must be in cache, used
3825
3668
            as a basis for delta when no other base is available
3826
3669
        :param cache: A cache of RevisionTrees that we can use.
3827
 
        :param a_graph: A Graph object to determine the heads() of the
3828
 
            rich-root data stream.
3829
3670
        :return: The revision_id of the last converted tree. The RevisionTree
3830
3671
            for it will be in cache
3831
3672
        """
3899
3740
        if root_keys_to_create:
3900
3741
            root_stream = _mod_fetch._new_root_data_stream(
3901
3742
                root_keys_to_create, self._revision_id_to_root_id, parent_map,
3902
 
                self.source, graph=a_graph)
 
3743
                self.source)
3903
3744
            to_texts.insert_record_stream(root_stream)
3904
3745
        to_texts.insert_record_stream(from_texts.get_record_stream(
3905
3746
            text_keys, self.target._format._fetch_order,
3962
3803
        cache[basis_id] = basis_tree
3963
3804
        del basis_tree # We don't want to hang on to it here
3964
3805
        hints = []
3965
 
        if self._converting_to_rich_root and len(revision_ids) > 100:
3966
 
            a_graph = _mod_fetch._get_rich_root_heads_graph(self.source,
3967
 
                                                            revision_ids)
3968
 
        else:
3969
 
            a_graph = None
 
3806
        a_graph = None
3970
3807
 
3971
3808
        for offset in range(0, len(revision_ids), batch_size):
3972
3809
            self.target.start_write_group()
3974
3811
                pb.update('Transferring revisions', offset,
3975
3812
                          len(revision_ids))
3976
3813
                batch = revision_ids[offset:offset+batch_size]
3977
 
                basis_id = self._fetch_batch(batch, basis_id, cache,
3978
 
                                             a_graph=a_graph)
 
3814
                basis_id = self._fetch_batch(batch, basis_id, cache)
3979
3815
            except:
3980
3816
                self.source._safe_to_return_from_cache = False
3981
3817
                self.target.abort_write_group()
3995
3831
        """See InterRepository.fetch()."""
3996
3832
        if fetch_spec is not None:
3997
3833
            raise AssertionError("Not implemented yet...")
3998
 
        # See <https://launchpad.net/bugs/456077> asking for a warning here
3999
 
        #
4000
 
        # nb this is only active for local-local fetches; other things using
4001
 
        # streaming.
4002
 
        ui.ui_factory.warn_cross_format_fetch(self.source._format,
4003
 
            self.target._format)
4004
3834
        ui.ui_factory.warn_experimental_format_fetch(self)
4005
3835
        if (not self.source.supports_rich_root()
4006
3836
            and self.target.supports_rich_root()):
4008
3838
            self._revision_id_to_root_id = {}
4009
3839
        else:
4010
3840
            self._converting_to_rich_root = False
 
3841
        # See <https://launchpad.net/bugs/456077> asking for a warning here
 
3842
        if self.source._format.network_name() != self.target._format.network_name():
 
3843
            ui.ui_factory.show_user_warning('cross_format_fetch',
 
3844
                from_format=self.source._format,
 
3845
                to_format=self.target._format)
4011
3846
        revision_ids = self.target.search_missing_revision_ids(self.source,
4012
3847
            revision_id, find_ghosts=find_ghosts).get_keys()
4013
3848
        if not revision_ids:
4048
3883
            basis_id = first_rev.parent_ids[0]
4049
3884
            # only valid as a basis if the target has it
4050
3885
            self.target.get_revision(basis_id)
4051
 
            # Try to get a basis tree - if its a ghost it will hit the
 
3886
            # Try to get a basis tree - if it's a ghost it will hit the
4052
3887
            # NoSuchRevision case.
4053
3888
            basis_tree = self.source.revision_tree(basis_id)
4054
3889
        except (IndexError, errors.NoSuchRevision):
4059
3894
 
4060
3895
InterRepository.register_optimiser(InterDifferingSerializer)
4061
3896
InterRepository.register_optimiser(InterSameDataRepository)
4062
 
InterRepository.register_optimiser(InterWeaveRepo)
4063
 
InterRepository.register_optimiser(InterKnitRepo)
4064
3897
 
4065
3898
 
4066
3899
class CopyConverter(object):
4254
4087
                is_resume = False
4255
4088
            try:
4256
4089
                # locked_insert_stream performs a commit|suspend.
4257
 
                return self._locked_insert_stream(stream, src_format, is_resume)
 
4090
                return self._locked_insert_stream(stream, src_format,
 
4091
                    is_resume)
4258
4092
            except:
4259
4093
                self.target_repo.abort_write_group(suppress_errors=True)
4260
4094
                raise
4296
4130
                    self._extract_and_insert_inventories(
4297
4131
                        substream, src_serializer)
4298
4132
            elif substream_type == 'inventory-deltas':
4299
 
                ui.ui_factory.warn_cross_format_fetch(src_format,
4300
 
                    self.target_repo._format)
4301
4133
                self._extract_and_insert_inventory_deltas(
4302
4134
                    substream, src_serializer)
4303
4135
            elif substream_type == 'chk_bytes':
4309
4141
                # required if the serializers are different only in terms of
4310
4142
                # the inventory.
4311
4143
                if src_serializer == to_serializer:
4312
 
                    self.target_repo.revisions.insert_record_stream(
4313
 
                        substream)
 
4144
                    self.target_repo.revisions.insert_record_stream(substream)
4314
4145
                else:
4315
4146
                    self._extract_and_insert_revisions(substream,
4316
4147
                        src_serializer)
4424
4255
        """Create a StreamSource streaming from from_repository."""
4425
4256
        self.from_repository = from_repository
4426
4257
        self.to_format = to_format
 
4258
        self._record_counter = RecordCounter()
4427
4259
 
4428
4260
    def delta_on_metadata(self):
4429
4261
        """Return True if delta's are permitted on metadata streams.