/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

  • Committer: Martin Pool
  • Date: 2009-03-13 07:54:48 UTC
  • mfrom: (4144 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4189.
  • Revision ID: mbp@sourcefrog.net-20090313075448-jlz1t7baz7gzipqn
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
47
47
from bzrlib.testament import Testament
48
48
""")
49
49
 
50
 
from bzrlib import registry
51
50
from bzrlib.decorators import needs_read_lock, needs_write_lock
52
51
from bzrlib.inter import InterObject
53
52
from bzrlib.inventory import Inventory, InventoryDirectory, ROOT_ID
 
53
from bzrlib import registry
54
54
from bzrlib.symbol_versioning import (
55
55
        deprecated_method,
56
56
        )
196
196
 
197
197
    def _generate_revision_if_needed(self):
198
198
        """Create a revision id if None was supplied.
199
 
        
 
199
 
200
200
        If the repository can not support user-specified revision ids
201
201
        they should override this function and raise CannotSetRevisionId
202
202
        if _new_revision_id is not None.
298
298
        :param parent_invs: The inventories of the parent revisions of the
299
299
            commit.
300
300
        :param path: The path the entry is at in the tree.
301
 
        :param tree: The tree which contains this entry and should be used to 
 
301
        :param tree: The tree which contains this entry and should be used to
302
302
            obtain content.
303
303
        :param content_summary: Summary data from the tree about the paths
304
304
            content - stat, length, exec, sha/link target. This is only
511
511
 
512
512
class RootCommitBuilder(CommitBuilder):
513
513
    """This commitbuilder actually records the root id"""
514
 
    
 
514
 
515
515
    # the root entry gets versioned properly by this builder.
516
516
    _versioned_root = True
517
517
 
610
610
 
611
611
    def _abort_write_group(self):
612
612
        """Template method for per-repository write group cleanup.
613
 
        
614
 
        This is called during abort before the write group is considered to be 
 
613
 
 
614
        This is called during abort before the write group is considered to be
615
615
        finished and should cleanup any internal state accrued during the write
616
616
        group. There is no requirement that data handed to the repository be
617
617
        *not* made available - this is not a rollback - but neither should any
623
623
 
624
624
    def add_fallback_repository(self, repository):
625
625
        """Add a repository to use for looking up data not held locally.
626
 
        
 
626
 
627
627
        :param repository: A repository.
628
628
        """
629
629
        if not self._format.supports_external_lookups:
634
634
        self.inventories.add_fallback_versioned_files(repository.inventories)
635
635
        self.revisions.add_fallback_versioned_files(repository.revisions)
636
636
        self.signatures.add_fallback_versioned_files(repository.signatures)
637
 
        self._fetch_order = 'topological'
638
637
 
639
638
    def _check_fallback_repository(self, repository):
640
639
        """Check that this repository can fallback to repository safely.
641
640
 
642
641
        Raise an error if not.
643
 
        
 
642
 
644
643
        :param repository: A repository to fallback to.
645
644
        """
646
645
        return InterRepository._assert_same_model(self, repository)
647
646
 
648
647
    def add_inventory(self, revision_id, inv, parents):
649
648
        """Add the inventory inv to the repository as revision_id.
650
 
        
 
649
 
651
650
        :param parents: The revision ids of the parents that revision_id
652
651
                        is known to have and are in the repository already.
653
652
 
755
754
        self.revisions.add_lines(key, parents, osutils.split_lines(text))
756
755
 
757
756
    def all_revision_ids(self):
758
 
        """Returns a list of all the revision ids in the repository. 
 
757
        """Returns a list of all the revision ids in the repository.
759
758
 
760
759
        This is conceptually deprecated because code should generally work on
761
760
        the graph reachable from a particular revision, and ignore any other
767
766
        return self._all_revision_ids()
768
767
 
769
768
    def _all_revision_ids(self):
770
 
        """Returns a list of all the revision ids in the repository. 
 
769
        """Returns a list of all the revision ids in the repository.
771
770
 
772
 
        These are in as much topological order as the underlying store can 
 
771
        These are in as much topological order as the underlying store can
773
772
        present.
774
773
        """
775
774
        raise NotImplementedError(self._all_revision_ids)
820
819
        self._reconcile_does_inventory_gc = True
821
820
        self._reconcile_fixes_text_parents = False
822
821
        self._reconcile_backsup_inventory = True
823
 
        # not right yet - should be more semantically clear ? 
824
 
        # 
 
822
        # not right yet - should be more semantically clear ?
 
823
        #
825
824
        # TODO: make sure to construct the right store classes, etc, depending
826
825
        # on whether escaping is required.
827
826
        self._warn_if_deprecated()
828
827
        self._write_group = None
829
828
        # Additional places to query for data.
830
829
        self._fallback_repositories = []
831
 
        # What order should fetch operations request streams in?
832
 
        # The default is unordered as that is the cheapest for an origin to
833
 
        # provide.
834
 
        self._fetch_order = 'unordered'
835
 
        # Does this repository use deltas that can be fetched as-deltas ?
836
 
        # (E.g. knits, where the knit deltas can be transplanted intact.
837
 
        # We default to False, which will ensure that enough data to get
838
 
        # a full text out of any fetch stream will be grabbed.
839
 
        self._fetch_uses_deltas = False
840
 
        # Should fetch trigger a reconcile after the fetch? Only needed for
841
 
        # some repository formats that can suffer internal inconsistencies.
842
 
        self._fetch_reconcile = False
843
830
        # An InventoryEntry cache, used during deserialization
844
831
        self._inventory_entry_cache = fifo_cache.FIFOCache(10*1024)
845
832
 
878
865
        This causes caching within the repository obejct to start accumlating
879
866
        data during reads, and allows a 'write_group' to be obtained. Write
880
867
        groups must be used for actual data insertion.
881
 
        
 
868
 
882
869
        :param token: if this is already locked, then lock_write will fail
883
870
            unless the token matches the existing lock.
884
871
        :returns: a token if this instance supports tokens, otherwise None.
913
900
    def leave_lock_in_place(self):
914
901
        """Tell this repository not to release the physical lock when this
915
902
        object is unlocked.
916
 
        
 
903
 
917
904
        If lock_write doesn't return a token, then this method is not supported.
918
905
        """
919
906
        self.control_files.leave_in_place()
1025
1012
    @needs_read_lock
1026
1013
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1027
1014
        """Return the revision ids that other has that this does not.
1028
 
        
 
1015
 
1029
1016
        These are returned in topological order.
1030
1017
 
1031
1018
        revision_id: only return revision ids included by revision_id.
1045
1032
 
1046
1033
    def copy_content_into(self, destination, revision_id=None):
1047
1034
        """Make a complete copy of the content in self into destination.
1048
 
        
1049
 
        This is a destructive operation! Do not use it on existing 
 
1035
 
 
1036
        This is a destructive operation! Do not use it on existing
1050
1037
        repositories.
1051
1038
        """
1052
1039
        return InterRepository.get(self, destination).copy_content(revision_id)
1066
1053
 
1067
1054
    def _commit_write_group(self):
1068
1055
        """Template method for per-repository write group cleanup.
1069
 
        
1070
 
        This is called before the write group is considered to be 
 
1056
 
 
1057
        This is called before the write group is considered to be
1071
1058
        finished and should ensure that all data handed to the repository
1072
 
        for writing during the write group is safely committed (to the 
 
1059
        for writing during the write group is safely committed (to the
1073
1060
        extent possible considering file system caching etc).
1074
1061
        """
1075
1062
 
1076
 
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False):
 
1063
    def suspend_write_group(self):
 
1064
        raise errors.UnsuspendableWriteGroup(self)
 
1065
 
 
1066
    def resume_write_group(self, tokens):
 
1067
        if not self.is_write_locked():
 
1068
            raise errors.NotWriteLocked(self)
 
1069
        if self._write_group:
 
1070
            raise errors.BzrError('already in a write group')
 
1071
        self._resume_write_group(tokens)
 
1072
        # so we can detect unlock/relock - the write group is now entered.
 
1073
        self._write_group = self.get_transaction()
 
1074
 
 
1075
    def _resume_write_group(self, tokens):
 
1076
        raise errors.UnsuspendableWriteGroup(self)
 
1077
 
 
1078
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
 
1079
            fetch_spec=None):
1077
1080
        """Fetch the content required to construct revision_id from source.
1078
1081
 
1079
 
        If revision_id is None all content is copied.
 
1082
        If revision_id is None and fetch_spec is None, then all content is
 
1083
        copied.
 
1084
 
1080
1085
        :param find_ghosts: Find and copy revisions in the source that are
1081
1086
            ghosts in the target (and not reachable directly by walking out to
1082
1087
            the first-present revision in target from revision_id).
 
1088
        :param revision_id: If specified, all the content needed for this
 
1089
            revision ID will be copied to the target.  Fetch will determine for
 
1090
            itself which content needs to be copied.
 
1091
        :param fetch_spec: If specified, a SearchResult or
 
1092
            PendingAncestryResult that describes which revisions to copy.  This
 
1093
            allows copying multiple heads at once.  Mutually exclusive with
 
1094
            revision_id.
1083
1095
        """
 
1096
        if fetch_spec is not None and revision_id is not None:
 
1097
            raise AssertionError(
 
1098
                "fetch_spec and revision_id are mutually exclusive.")
1084
1099
        # fast path same-url fetch operations
1085
 
        if self.has_same_location(source):
 
1100
        if self.has_same_location(source) and fetch_spec is None:
1086
1101
            # check that last_revision is in 'from' and then return a
1087
1102
            # no-operation.
1088
1103
            if (revision_id is not None and
1094
1109
        # IncompatibleRepositories when asked to fetch.
1095
1110
        inter = InterRepository.get(source, self)
1096
1111
        return inter.fetch(revision_id=revision_id, pb=pb,
1097
 
            find_ghosts=find_ghosts)
 
1112
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1098
1113
 
1099
1114
    def create_bundle(self, target, base, fileobj, format=None):
1100
1115
        return serializer.write_bundle(self, target, base, fileobj, format)
1103
1118
                           timezone=None, committer=None, revprops=None,
1104
1119
                           revision_id=None):
1105
1120
        """Obtain a CommitBuilder for this repository.
1106
 
        
 
1121
 
1107
1122
        :param branch: Branch to commit to.
1108
1123
        :param parents: Revision ids of the parents of the new revision.
1109
1124
        :param config: Configuration to use.
1171
1186
 
1172
1187
    def _start_write_group(self):
1173
1188
        """Template method for per-repository write group startup.
1174
 
        
1175
 
        This is called before the write group is considered to be 
 
1189
 
 
1190
        This is called before the write group is considered to be
1176
1191
        entered.
1177
1192
        """
1178
1193
 
1199
1214
                dest_repo = a_bzrdir.open_repository()
1200
1215
        return dest_repo
1201
1216
 
 
1217
    def _get_sink(self):
 
1218
        """Return a sink for streaming into this repository."""
 
1219
        return StreamSink(self)
 
1220
 
 
1221
    def _get_source(self, to_format):
 
1222
        """Return a source for streaming from this repository."""
 
1223
        return StreamSource(self, to_format)
 
1224
 
1202
1225
    @needs_read_lock
1203
1226
    def has_revision(self, revision_id):
1204
1227
        """True if this repository has a copy of the revision."""
1227
1250
    @needs_read_lock
1228
1251
    def get_revision_reconcile(self, revision_id):
1229
1252
        """'reconcile' helper routine that allows access to a revision always.
1230
 
        
 
1253
 
1231
1254
        This variant of get_revision does not cross check the weave graph
1232
1255
        against the revision one as get_revision does: but it should only
1233
1256
        be used by reconcile, or reconcile-alike commands that are correcting
1271
1294
 
1272
1295
    def get_deltas_for_revisions(self, revisions):
1273
1296
        """Produce a generator of revision deltas.
1274
 
        
 
1297
 
1275
1298
        Note that the input is a sequence of REVISIONS, not revision_ids.
1276
1299
        Trees will be held in memory until the generator exits.
1277
1300
        Each delta is relative to the revision's lefthand predecessor.
1280
1303
        for revision in revisions:
1281
1304
            required_trees.add(revision.revision_id)
1282
1305
            required_trees.update(revision.parent_ids[:1])
1283
 
        trees = dict((t.get_revision_id(), t) for 
 
1306
        trees = dict((t.get_revision_id(), t) for
1284
1307
                     t in self.revision_trees(required_trees))
1285
1308
        for revision in revisions:
1286
1309
            if not revision.parent_ids:
1351
1374
 
1352
1375
        # this code needs to read every new line in every inventory for the
1353
1376
        # inventories [revision_ids]. Seeing a line twice is ok. Seeing a line
1354
 
        # not present in one of those inventories is unnecessary but not 
 
1377
        # not present in one of those inventories is unnecessary but not
1355
1378
        # harmful because we are filtering by the revision id marker in the
1356
 
        # inventory lines : we only select file ids altered in one of those  
 
1379
        # inventory lines : we only select file ids altered in one of those
1357
1380
        # revisions. We don't need to see all lines in the inventory because
1358
1381
        # only those added in an inventory in rev X can contain a revision=X
1359
1382
        # line.
1409
1432
                result[key] = True
1410
1433
        return result
1411
1434
 
 
1435
    def _inventory_xml_lines_for_keys(self, keys):
 
1436
        """Get a line iterator of the sort needed for findind references.
 
1437
 
 
1438
        Not relevant for non-xml inventory repositories.
 
1439
 
 
1440
        Ghosts in revision_keys are ignored.
 
1441
 
 
1442
        :param revision_keys: The revision keys for the inventories to inspect.
 
1443
        :return: An iterator over (inventory line, revid) for the fulltexts of
 
1444
            all of the xml inventories specified by revision_keys.
 
1445
        """
 
1446
        stream = self.inventories.get_record_stream(keys, 'unordered', True)
 
1447
        for record in stream:
 
1448
            if record.storage_kind != 'absent':
 
1449
                chunks = record.get_bytes_as('chunked')
 
1450
                revid = record.key[-1]
 
1451
                lines = osutils.chunks_to_lines(chunks)
 
1452
                for line in lines:
 
1453
                    yield line, revid
 
1454
 
1412
1455
    def _find_file_ids_from_xml_inventory_lines(self, line_iterator,
1413
1456
        revision_ids):
1414
1457
        """Helper routine for fileids_altered_by_revision_ids.
1424
1467
        revision_ids. Each altered file-ids has the exact revision_ids that
1425
1468
        altered it listed explicitly.
1426
1469
        """
 
1470
        seen = set(self._find_text_key_references_from_xml_inventory_lines(
 
1471
                line_iterator).iterkeys())
 
1472
        # Note that revision_ids are revision keys.
 
1473
        parent_maps = self.revisions.get_parent_map(revision_ids)
 
1474
        parents = set()
 
1475
        map(parents.update, parent_maps.itervalues())
 
1476
        parents.difference_update(revision_ids)
 
1477
        parent_seen = set(self._find_text_key_references_from_xml_inventory_lines(
 
1478
            self._inventory_xml_lines_for_keys(parents)))
 
1479
        new_keys = seen - parent_seen
1427
1480
        result = {}
1428
1481
        setdefault = result.setdefault
1429
 
        for key in \
1430
 
            self._find_text_key_references_from_xml_inventory_lines(
1431
 
                line_iterator).iterkeys():
1432
 
            # once data is all ensured-consistent; then this is
1433
 
            # if revision_id == version_id
1434
 
            if key[-1:] in revision_ids:
1435
 
                setdefault(key[0], set()).add(key[-1])
 
1482
        for key in new_keys:
 
1483
            setdefault(key[0], set()).add(key[-1])
1436
1484
        return result
1437
1485
 
1438
1486
    def fileids_altered_by_revision_ids(self, revision_ids, _inv_weave=None):
1536
1584
        batch_size = 10 # should be ~150MB on a 55K path tree
1537
1585
        batch_count = len(revision_order) / batch_size + 1
1538
1586
        processed_texts = 0
1539
 
        pb.update("Calculating text parents.", processed_texts, text_count)
 
1587
        pb.update("Calculating text parents", processed_texts, text_count)
1540
1588
        for offset in xrange(batch_count):
1541
1589
            to_query = revision_order[offset * batch_size:(offset + 1) *
1542
1590
                batch_size]
1546
1594
                revision_id = rev_tree.get_revision_id()
1547
1595
                parent_ids = ancestors[revision_id]
1548
1596
                for text_key in revision_keys[revision_id]:
1549
 
                    pb.update("Calculating text parents.", processed_texts)
 
1597
                    pb.update("Calculating text parents", processed_texts)
1550
1598
                    processed_texts += 1
1551
1599
                    candidate_parents = []
1552
1600
                    for parent_id in parent_ids:
1674
1722
            yield ''.join(chunks), key[-1]
1675
1723
 
1676
1724
    def deserialise_inventory(self, revision_id, xml):
1677
 
        """Transform the xml into an inventory object. 
 
1725
        """Transform the xml into an inventory object.
1678
1726
 
1679
1727
        :param revision_id: The expected revision id of the inventory.
1680
1728
        :param xml: A serialised inventory.
1782
1830
        # TODO: refactor this to use an existing revision object
1783
1831
        # so we don't need to read it in twice.
1784
1832
        if revision_id == _mod_revision.NULL_REVISION:
1785
 
            return RevisionTree(self, Inventory(root_id=None), 
 
1833
            return RevisionTree(self, Inventory(root_id=None),
1786
1834
                                _mod_revision.NULL_REVISION)
1787
1835
        else:
1788
1836
            inv = self.get_revision_inventory(revision_id)
1800
1848
    def get_ancestry(self, revision_id, topo_sorted=True):
1801
1849
        """Return a list of revision-ids integrated by a revision.
1802
1850
 
1803
 
        The first element of the list is always None, indicating the origin 
1804
 
        revision.  This might change when we have history horizons, or 
 
1851
        The first element of the list is always None, indicating the origin
 
1852
        revision.  This might change when we have history horizons, or
1805
1853
        perhaps we should have a new API.
1806
 
        
 
1854
 
1807
1855
        This is topologically sorted.
1808
1856
        """
1809
1857
        if _mod_revision.is_null(revision_id):
1833
1881
        types it should be a no-op that just returns.
1834
1882
 
1835
1883
        This stub method does not require a lock, but subclasses should use
1836
 
        @needs_write_lock as this is a long running call its reasonable to 
 
1884
        @needs_write_lock as this is a long running call its reasonable to
1837
1885
        implicitly lock for the user.
1838
1886
        """
1839
1887
 
1902
1950
                          working trees.
1903
1951
        """
1904
1952
        raise NotImplementedError(self.set_make_working_trees)
1905
 
    
 
1953
 
1906
1954
    def make_working_trees(self):
1907
1955
        """Returns the policy for making working trees on new branches."""
1908
1956
        raise NotImplementedError(self.make_working_trees)
1973
2021
                    revision_id.decode('ascii')
1974
2022
                except UnicodeDecodeError:
1975
2023
                    raise errors.NonAsciiRevisionId(method, self)
1976
 
    
 
2024
 
1977
2025
    def revision_graph_can_have_wrong_parents(self):
1978
2026
        """Is it possible for this repository to have a revision graph with
1979
2027
        incorrect parents?
2097
2145
 
2098
2146
class MetaDirRepository(Repository):
2099
2147
    """Repositories in the new meta-dir layout.
2100
 
    
 
2148
 
2101
2149
    :ivar _transport: Transport for access to repository control files,
2102
2150
        typically pointing to .bzr/repository.
2103
2151
    """
2128
2176
        else:
2129
2177
            self._transport.put_bytes('no-working-trees', '',
2130
2178
                mode=self.bzrdir._get_file_mode())
2131
 
    
 
2179
 
2132
2180
    def make_working_trees(self):
2133
2181
        """Returns the policy for making working trees on new branches."""
2134
2182
        return not self._transport.has('no-working-trees')
2142
2190
            control_files)
2143
2191
 
2144
2192
 
2145
 
class RepositoryFormatRegistry(registry.Registry):
2146
 
    """Registry of RepositoryFormats."""
2147
 
 
2148
 
    def get(self, format_string):
2149
 
        r = registry.Registry.get(self, format_string)
2150
 
        if callable(r):
2151
 
            r = r()
2152
 
        return r
2153
 
    
2154
 
 
2155
 
format_registry = RepositoryFormatRegistry()
2156
 
"""Registry of formats, indexed by their identifying format string.
 
2193
network_format_registry = registry.FormatRegistry()
 
2194
"""Registry of formats indexed by their network name.
 
2195
 
 
2196
The network name for a repository format is an identifier that can be used when
 
2197
referring to formats with smart server operations. See
 
2198
RepositoryFormat.network_name() for more detail.
 
2199
"""
 
2200
 
 
2201
 
 
2202
format_registry = registry.FormatRegistry(network_format_registry)
 
2203
"""Registry of formats, indexed by their BzrDirMetaFormat format string.
2157
2204
 
2158
2205
This can contain either format instances themselves, or classes/factories that
2159
2206
can be called to obtain one.
2166
2213
class RepositoryFormat(object):
2167
2214
    """A repository format.
2168
2215
 
2169
 
    Formats provide three things:
 
2216
    Formats provide four things:
2170
2217
     * An initialization routine to construct repository data on disk.
2171
 
     * a format string which is used when the BzrDir supports versioned
2172
 
       children.
 
2218
     * a optional format string which is used when the BzrDir supports
 
2219
       versioned children.
2173
2220
     * an open routine which returns a Repository instance.
 
2221
     * A network name for referring to the format in smart server RPC
 
2222
       methods.
2174
2223
 
2175
2224
    There is one and only one Format subclass for each on-disk format. But
2176
2225
    there can be one Repository subclass that is used for several different
2177
2226
    formats. The _format attribute on a Repository instance can be used to
2178
2227
    determine the disk format.
2179
2228
 
2180
 
    Formats are placed in an dict by their format string for reference 
2181
 
    during opening. These should be subclasses of RepositoryFormat
2182
 
    for consistency.
 
2229
    Formats are placed in a registry by their format string for reference
 
2230
    during opening. These should be subclasses of RepositoryFormat for
 
2231
    consistency.
2183
2232
 
2184
2233
    Once a format is deprecated, just deprecate the initialize and open
2185
 
    methods on the format class. Do not deprecate the object, as the 
2186
 
    object will be created every system load.
 
2234
    methods on the format class. Do not deprecate the object, as the
 
2235
    object may be created even when a repository instnace hasn't been
 
2236
    created.
2187
2237
 
2188
2238
    Common instance attributes:
2189
2239
    _matchingbzrdir - the bzrdir format that the repository format was
2198
2248
    # Can this repository be given external locations to lookup additional
2199
2249
    # data. Set to True or False in derived classes.
2200
2250
    supports_external_lookups = None
 
2251
    # What order should fetch operations request streams in?
 
2252
    # The default is unordered as that is the cheapest for an origin to
 
2253
    # provide.
 
2254
    _fetch_order = 'unordered'
 
2255
    # Does this repository format use deltas that can be fetched as-deltas ?
 
2256
    # (E.g. knits, where the knit deltas can be transplanted intact.
 
2257
    # We default to False, which will ensure that enough data to get
 
2258
    # a full text out of any fetch stream will be grabbed.
 
2259
    _fetch_uses_deltas = False
 
2260
    # Should fetch trigger a reconcile after the fetch? Only needed for
 
2261
    # some repository formats that can suffer internal inconsistencies.
 
2262
    _fetch_reconcile = False
2201
2263
 
2202
2264
    def __str__(self):
2203
2265
        return "<%s>" % self.__class__.__name__
2212
2274
    @classmethod
2213
2275
    def find_format(klass, a_bzrdir):
2214
2276
        """Return the format for the repository object in a_bzrdir.
2215
 
        
 
2277
 
2216
2278
        This is used by bzr native formats that have a "format" file in
2217
 
        the repository.  Other methods may be used by different types of 
 
2279
        the repository.  Other methods may be used by different types of
2218
2280
        control directory.
2219
2281
        """
2220
2282
        try:
2234
2296
    @classmethod
2235
2297
    def unregister_format(klass, format):
2236
2298
        format_registry.remove(format.get_format_string())
2237
 
    
 
2299
 
2238
2300
    @classmethod
2239
2301
    def get_default_format(klass):
2240
2302
        """Return the current default format."""
2243
2305
 
2244
2306
    def get_format_string(self):
2245
2307
        """Return the ASCII format string that identifies this format.
2246
 
        
2247
 
        Note that in pre format ?? repositories the format string is 
 
2308
 
 
2309
        Note that in pre format ?? repositories the format string is
2248
2310
        not permitted nor written to disk.
2249
2311
        """
2250
2312
        raise NotImplementedError(self.get_format_string)
2281
2343
        :param a_bzrdir: The bzrdir to put the new repository in it.
2282
2344
        :param shared: The repository should be initialized as a sharable one.
2283
2345
        :returns: The new repository object.
2284
 
        
 
2346
 
2285
2347
        This may raise UninitializableFormat if shared repository are not
2286
2348
        compatible the a_bzrdir.
2287
2349
        """
2291
2353
        """Is this format supported?
2292
2354
 
2293
2355
        Supported formats must be initializable and openable.
2294
 
        Unsupported formats may not support initialization or committing or 
 
2356
        Unsupported formats may not support initialization or committing or
2295
2357
        some other features depending on the reason for not being supported.
2296
2358
        """
2297
2359
        return True
2298
2360
 
 
2361
    def network_name(self):
 
2362
        """A simple byte string uniquely identifying this format for RPC calls.
 
2363
 
 
2364
        MetaDir repository formats use their disk format string to identify the
 
2365
        repository over the wire. All in one formats such as bzr < 0.8, and
 
2366
        foreign formats like svn/git and hg should use some marker which is
 
2367
        unique and immutable.
 
2368
        """
 
2369
        raise NotImplementedError(self.network_name)
 
2370
 
2299
2371
    def check_conversion_target(self, target_format):
2300
2372
        raise NotImplementedError(self.check_conversion_target)
2301
2373
 
2302
2374
    def open(self, a_bzrdir, _found=False):
2303
2375
        """Return an instance of this format for the bzrdir a_bzrdir.
2304
 
        
 
2376
 
2305
2377
        _found is a private parameter, do not use it.
2306
2378
        """
2307
2379
        raise NotImplementedError(self.open)
2351
2423
        finally:
2352
2424
            control_files.unlock()
2353
2425
 
2354
 
 
2355
 
# formats which have no format string are not discoverable
2356
 
# and not independently creatable, so are not registered.  They're 
 
2426
    def network_name(self):
 
2427
        """Metadir formats have matching disk and network format strings."""
 
2428
        return self.get_format_string()
 
2429
 
 
2430
 
 
2431
# Pre-0.8 formats that don't have a disk format string (because they are
 
2432
# versioned by the matching control directory). We use the control directories
 
2433
# disk format string as a key for the network_name because they meet the
 
2434
# constraints (simple string, unique, immmutable).
 
2435
network_format_registry.register_lazy(
 
2436
    "Bazaar-NG branch, format 5\n",
 
2437
    'bzrlib.repofmt.weaverepo',
 
2438
    'RepositoryFormat5',
 
2439
)
 
2440
network_format_registry.register_lazy(
 
2441
    "Bazaar-NG branch, format 6\n",
 
2442
    'bzrlib.repofmt.weaverepo',
 
2443
    'RepositoryFormat6',
 
2444
)
 
2445
 
 
2446
# formats which have no format string are not discoverable or independently
 
2447
# creatable on disk, so are not registered in format_registry.  They're
2357
2448
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
2358
2449
# needed, it's constructed directly by the BzrDir.  Non-native formats where
2359
2450
# the repository is not separately opened are similar.
2426
2517
    'RepositoryFormatKnitPack6RichRoot',
2427
2518
    )
2428
2519
 
2429
 
# Development formats. 
 
2520
# Development formats.
2430
2521
# 1.7->1.8 go below here
2431
2522
format_registry.register_lazy(
2432
2523
    "Bazaar development format 2 (needs bzr.dev from before 1.8)\n",
2445
2536
    """This class represents operations taking place between two repositories.
2446
2537
 
2447
2538
    Its instances have methods like copy_content and fetch, and contain
2448
 
    references to the source and target repositories these operations can be 
 
2539
    references to the source and target repositories these operations can be
2449
2540
    carried out on.
2450
2541
 
2451
2542
    Often we will provide convenience methods on 'repository' which carry out
2464
2555
        self.target_get_graph = self.target.get_graph
2465
2556
        self.target_get_parent_map = self.target.get_parent_map
2466
2557
 
 
2558
    @needs_write_lock
2467
2559
    def copy_content(self, revision_id=None):
2468
 
        raise NotImplementedError(self.copy_content)
2469
 
 
2470
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
 
2560
        """Make a complete copy of the content in self into destination.
 
2561
 
 
2562
        This is a destructive operation! Do not use it on existing
 
2563
        repositories.
 
2564
 
 
2565
        :param revision_id: Only copy the content needed to construct
 
2566
                            revision_id and its parents.
 
2567
        """
 
2568
        try:
 
2569
            self.target.set_make_working_trees(self.source.make_working_trees())
 
2570
        except NotImplementedError:
 
2571
            pass
 
2572
        self.target.fetch(self.source, revision_id=revision_id)
 
2573
 
 
2574
    @needs_write_lock
 
2575
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
 
2576
            fetch_spec=None):
2471
2577
        """Fetch the content required to construct revision_id.
2472
2578
 
2473
2579
        The content is copied from self.source to self.target.
2476
2582
                            content is copied.
2477
2583
        :param pb: optional progress bar to use for progress reports. If not
2478
2584
                   provided a default one will be created.
2479
 
 
2480
 
        :returns: (copied_revision_count, failures).
 
2585
        :return: None.
2481
2586
        """
2482
 
        # Normally we should find a specific InterRepository subclass to do
2483
 
        # the fetch; if nothing else then at least InterSameDataRepository.
2484
 
        # If none of them is suitable it looks like fetching is not possible;
2485
 
        # we try to give a good message why.  _assert_same_model will probably
2486
 
        # give a helpful message; otherwise a generic one.
2487
 
        self._assert_same_model(self.source, self.target)
2488
 
        raise errors.IncompatibleRepositories(self.source, self.target,
2489
 
            "no suitableInterRepository found")
 
2587
        from bzrlib.fetch import RepoFetcher
 
2588
        f = RepoFetcher(to_repository=self.target,
 
2589
                               from_repository=self.source,
 
2590
                               last_revision=revision_id,
 
2591
                               fetch_spec=fetch_spec,
 
2592
                               pb=pb, find_ghosts=find_ghosts)
2490
2593
 
2491
2594
    def _walk_to_common_revisions(self, revision_ids):
2492
2595
        """Walk out from revision_ids in source to revisions target has.
2550
2653
    @needs_read_lock
2551
2654
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2552
2655
        """Return the revision ids that source has that target does not.
2553
 
        
 
2656
 
2554
2657
        :param revision_id: only return revision ids included by this
2555
2658
                            revision_id.
2556
2659
        :param find_ghosts: If True find missing revisions in deep history
2575
2678
    @staticmethod
2576
2679
    def _same_model(source, target):
2577
2680
        """True if source and target have the same data representation.
2578
 
        
 
2681
 
2579
2682
        Note: this is always called on the base class; overriding it in a
2580
2683
        subclass will have no effect.
2581
2684
        """
2599
2702
 
2600
2703
class InterSameDataRepository(InterRepository):
2601
2704
    """Code for converting between repositories that represent the same data.
2602
 
    
 
2705
 
2603
2706
    Data format and model must match for this to work.
2604
2707
    """
2605
2708
 
2606
2709
    @classmethod
2607
2710
    def _get_repo_format_to_test(self):
2608
2711
        """Repository format for testing with.
2609
 
        
 
2712
 
2610
2713
        InterSameData can pull from subtree to subtree and from non-subtree to
2611
2714
        non-subtree, so we test this with the richest repository format.
2612
2715
        """
2617
2720
    def is_compatible(source, target):
2618
2721
        return InterRepository._same_model(source, target)
2619
2722
 
2620
 
    @needs_write_lock
2621
 
    def copy_content(self, revision_id=None):
2622
 
        """Make a complete copy of the content in self into destination.
2623
 
 
2624
 
        This copies both the repository's revision data, and configuration information
2625
 
        such as the make_working_trees setting.
2626
 
        
2627
 
        This is a destructive operation! Do not use it on existing 
2628
 
        repositories.
2629
 
 
2630
 
        :param revision_id: Only copy the content needed to construct
2631
 
                            revision_id and its parents.
2632
 
        """
2633
 
        try:
2634
 
            self.target.set_make_working_trees(self.source.make_working_trees())
2635
 
        except NotImplementedError:
2636
 
            pass
2637
 
        # but don't bother fetching if we have the needed data now.
2638
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
2639
 
            self.target.has_revision(revision_id)):
2640
 
            return
2641
 
        self.target.fetch(self.source, revision_id=revision_id)
2642
 
 
2643
 
    @needs_write_lock
2644
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2645
 
        """See InterRepository.fetch()."""
2646
 
        from bzrlib.fetch import RepoFetcher
2647
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2648
 
               self.source, self.source._format, self.target,
2649
 
               self.target._format)
2650
 
        f = RepoFetcher(to_repository=self.target,
2651
 
                               from_repository=self.source,
2652
 
                               last_revision=revision_id,
2653
 
                               pb=pb, find_ghosts=find_ghosts)
2654
 
        return f.count_copied, f.failed_revisions
2655
 
 
2656
2723
 
2657
2724
class InterWeaveRepo(InterSameDataRepository):
2658
2725
    """Optimised code paths between Weave based repositories.
2659
 
    
 
2726
 
2660
2727
    This should be in bzrlib/repofmt/weaverepo.py but we have not yet
2661
2728
    implemented lazy inter-object optimisation.
2662
2729
    """
2669
2736
    @staticmethod
2670
2737
    def is_compatible(source, target):
2671
2738
        """Be compatible with known Weave formats.
2672
 
        
 
2739
 
2673
2740
        We don't test for the stores being of specific types because that
2674
 
        could lead to confusing results, and there is no need to be 
 
2741
        could lead to confusing results, and there is no need to be
2675
2742
        overly general.
2676
2743
        """
2677
2744
        from bzrlib.repofmt.weaverepo import (
2688
2755
                                                RepositoryFormat7)))
2689
2756
        except AttributeError:
2690
2757
            return False
2691
 
    
 
2758
 
2692
2759
    @needs_write_lock
2693
2760
    def copy_content(self, revision_id=None):
2694
2761
        """See InterRepository.copy_content()."""
2721
2788
        else:
2722
2789
            self.target.fetch(self.source, revision_id=revision_id)
2723
2790
 
2724
 
    @needs_write_lock
2725
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2726
 
        """See InterRepository.fetch()."""
2727
 
        from bzrlib.fetch import RepoFetcher
2728
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2729
 
               self.source, self.source._format, self.target, self.target._format)
2730
 
        f = RepoFetcher(to_repository=self.target,
2731
 
                               from_repository=self.source,
2732
 
                               last_revision=revision_id,
2733
 
                               pb=pb, find_ghosts=find_ghosts)
2734
 
        return f.count_copied, f.failed_revisions
2735
 
 
2736
2791
    @needs_read_lock
2737
2792
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2738
2793
        """See InterRepository.missing_revision_ids()."""
2739
2794
        # we want all revisions to satisfy revision_id in source.
2740
2795
        # but we don't want to stat every file here and there.
2741
 
        # we want then, all revisions other needs to satisfy revision_id 
 
2796
        # we want then, all revisions other needs to satisfy revision_id
2742
2797
        # checked, but not those that we have locally.
2743
 
        # so the first thing is to get a subset of the revisions to 
 
2798
        # so the first thing is to get a subset of the revisions to
2744
2799
        # satisfy revision_id in source, and then eliminate those that
2745
 
        # we do already have. 
 
2800
        # we do already have.
2746
2801
        # this is slow on high latency connection to self, but as as this
2747
 
        # disk format scales terribly for push anyway due to rewriting 
 
2802
        # disk format scales terribly for push anyway due to rewriting
2748
2803
        # inventory.weave, this is considered acceptable.
2749
2804
        # - RBC 20060209
2750
2805
        if revision_id is not None:
2770
2825
            # and the tip revision was validated by get_ancestry.
2771
2826
            result_set = required_revisions
2772
2827
        else:
2773
 
            # if we just grabbed the possibly available ids, then 
 
2828
            # if we just grabbed the possibly available ids, then
2774
2829
            # we only have an estimate of whats available and need to validate
2775
2830
            # that against the revision records.
2776
2831
            result_set = set(
2789
2844
    @staticmethod
2790
2845
    def is_compatible(source, target):
2791
2846
        """Be compatible with known Knit formats.
2792
 
        
 
2847
 
2793
2848
        We don't test for the stores being of specific types because that
2794
 
        could lead to confusing results, and there is no need to be 
 
2849
        could lead to confusing results, and there is no need to be
2795
2850
        overly general.
2796
2851
        """
2797
2852
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
2802
2857
            return False
2803
2858
        return are_knits and InterRepository._same_model(source, target)
2804
2859
 
2805
 
    @needs_write_lock
2806
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2807
 
        """See InterRepository.fetch()."""
2808
 
        from bzrlib.fetch import RepoFetcher
2809
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2810
 
               self.source, self.source._format, self.target, self.target._format)
2811
 
        f = RepoFetcher(to_repository=self.target,
2812
 
                            from_repository=self.source,
2813
 
                            last_revision=revision_id,
2814
 
                            pb=pb, find_ghosts=find_ghosts)
2815
 
        return f.count_copied, f.failed_revisions
2816
 
 
2817
2860
    @needs_read_lock
2818
2861
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2819
2862
        """See InterRepository.missing_revision_ids()."""
2840
2883
            # and the tip revision was validated by get_ancestry.
2841
2884
            result_set = required_revisions
2842
2885
        else:
2843
 
            # if we just grabbed the possibly available ids, then 
 
2886
            # if we just grabbed the possibly available ids, then
2844
2887
            # we only have an estimate of whats available and need to validate
2845
2888
            # that against the revision records.
2846
2889
            result_set = set(
2859
2902
    @staticmethod
2860
2903
    def is_compatible(source, target):
2861
2904
        """Be compatible with known Pack formats.
2862
 
        
 
2905
 
2863
2906
        We don't test for the stores being of specific types because that
2864
 
        could lead to confusing results, and there is no need to be 
 
2907
        could lead to confusing results, and there is no need to be
2865
2908
        overly general.
2866
2909
        """
2867
2910
        from bzrlib.repofmt.pack_repo import RepositoryFormatPack
2873
2916
        return are_packs and InterRepository._same_model(source, target)
2874
2917
 
2875
2918
    @needs_write_lock
2876
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
 
2919
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
 
2920
            fetch_spec=None):
2877
2921
        """See InterRepository.fetch()."""
2878
2922
        if (len(self.source._fallback_repositories) > 0 or
2879
2923
            len(self.target._fallback_repositories) > 0):
2883
2927
            # attributes on repository.
2884
2928
            from bzrlib.fetch import RepoFetcher
2885
2929
            fetcher = RepoFetcher(self.target, self.source, revision_id,
2886
 
                                  pb, find_ghosts)
2887
 
            return fetcher.count_copied, fetcher.failed_revisions
2888
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2889
 
               self.source, self.source._format, self.target, self.target._format)
2890
 
        self.count_copied = 0
 
2930
                    pb, find_ghosts, fetch_spec=fetch_spec)
 
2931
        if fetch_spec is not None:
 
2932
            if len(list(fetch_spec.heads)) != 1:
 
2933
                raise AssertionError(
 
2934
                    "InterPackRepo.fetch doesn't support "
 
2935
                    "fetching multiple heads yet.")
 
2936
            revision_id = fetch_spec.heads[0]
 
2937
            fetch_spec = None
2891
2938
        if revision_id is None:
2892
2939
            # TODO:
2893
2940
            # everything to do - use pack logic
2942
2989
 
2943
2990
    def _autopack(self):
2944
2991
        self.target._pack_collection.autopack()
2945
 
        
 
2992
 
2946
2993
    def _get_target_pack_collection(self):
2947
2994
        return self.target._pack_collection
2948
2995
 
2949
2996
    @needs_read_lock
2950
2997
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2951
2998
        """See InterRepository.missing_revision_ids().
2952
 
        
 
2999
 
2953
3000
        :param find_ghosts: Find ghosts throughout the ancestry of
2954
3001
            revision_id.
2955
3002
        """
2986
3033
        return self.source.revision_ids_to_search_result(result_set)
2987
3034
 
2988
3035
 
2989
 
class InterModel1and2(InterRepository):
2990
 
 
2991
 
    @classmethod
2992
 
    def _get_repo_format_to_test(self):
2993
 
        return None
2994
 
 
2995
 
    @staticmethod
2996
 
    def is_compatible(source, target):
2997
 
        if not source.supports_rich_root() and target.supports_rich_root():
2998
 
            return True
2999
 
        else:
3000
 
            return False
3001
 
 
3002
 
    @needs_write_lock
3003
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3004
 
        """See InterRepository.fetch()."""
3005
 
        from bzrlib.fetch import Model1toKnit2Fetcher
3006
 
        f = Model1toKnit2Fetcher(to_repository=self.target,
3007
 
                                 from_repository=self.source,
3008
 
                                 last_revision=revision_id,
3009
 
                                 pb=pb, find_ghosts=find_ghosts)
3010
 
        return f.count_copied, f.failed_revisions
3011
 
 
3012
 
    @needs_write_lock
3013
 
    def copy_content(self, revision_id=None):
3014
 
        """Make a complete copy of the content in self into destination.
3015
 
        
3016
 
        This is a destructive operation! Do not use it on existing 
3017
 
        repositories.
3018
 
 
3019
 
        :param revision_id: Only copy the content needed to construct
3020
 
                            revision_id and its parents.
3021
 
        """
3022
 
        try:
3023
 
            self.target.set_make_working_trees(self.source.make_working_trees())
3024
 
        except NotImplementedError:
3025
 
            pass
3026
 
        # but don't bother fetching if we have the needed data now.
3027
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
3028
 
            self.target.has_revision(revision_id)):
3029
 
            return
3030
 
        self.target.fetch(self.source, revision_id=revision_id)
3031
 
 
3032
 
 
3033
 
class InterKnit1and2(InterKnitRepo):
3034
 
 
3035
 
    @classmethod
3036
 
    def _get_repo_format_to_test(self):
3037
 
        return None
3038
 
 
3039
 
    @staticmethod
3040
 
    def is_compatible(source, target):
3041
 
        """Be compatible with Knit1 source and Knit3 target"""
3042
 
        try:
3043
 
            from bzrlib.repofmt.knitrepo import (
3044
 
                RepositoryFormatKnit1,
3045
 
                RepositoryFormatKnit3,
3046
 
                )
3047
 
            from bzrlib.repofmt.pack_repo import (
3048
 
                RepositoryFormatKnitPack1,
3049
 
                RepositoryFormatKnitPack3,
3050
 
                RepositoryFormatKnitPack4,
3051
 
                RepositoryFormatKnitPack5,
3052
 
                RepositoryFormatKnitPack5RichRoot,
3053
 
                RepositoryFormatKnitPack6,
3054
 
                RepositoryFormatKnitPack6RichRoot,
3055
 
                RepositoryFormatPackDevelopment2,
3056
 
                RepositoryFormatPackDevelopment2Subtree,
3057
 
                )
3058
 
            norichroot = (
3059
 
                RepositoryFormatKnit1,            # no rr, no subtree
3060
 
                RepositoryFormatKnitPack1,        # no rr, no subtree
3061
 
                RepositoryFormatPackDevelopment2, # no rr, no subtree
3062
 
                RepositoryFormatKnitPack5,        # no rr, no subtree
3063
 
                RepositoryFormatKnitPack6,        # no rr, no subtree
3064
 
                )
3065
 
            richroot = (
3066
 
                RepositoryFormatKnit3,            # rr, subtree
3067
 
                RepositoryFormatKnitPack3,        # rr, subtree
3068
 
                RepositoryFormatKnitPack4,        # rr, no subtree
3069
 
                RepositoryFormatKnitPack5RichRoot,# rr, no subtree
3070
 
                RepositoryFormatKnitPack6RichRoot,# rr, no subtree
3071
 
                RepositoryFormatPackDevelopment2Subtree, # rr, subtree
3072
 
                )
3073
 
            for format in norichroot:
3074
 
                if format.rich_root_data:
3075
 
                    raise AssertionError('Format %s is a rich-root format'
3076
 
                        ' but is included in the non-rich-root list'
3077
 
                        % (format,))
3078
 
            for format in richroot:
3079
 
                if not format.rich_root_data:
3080
 
                    raise AssertionError('Format %s is not a rich-root format'
3081
 
                        ' but is included in the rich-root list'
3082
 
                        % (format,))
3083
 
            # TODO: One alternative is to just check format.rich_root_data,
3084
 
            #       instead of keeping membership lists. However, the formats
3085
 
            #       *also* have to use the same 'Knit' style of storage
3086
 
            #       (line-deltas, fulltexts, etc.)
3087
 
            return (isinstance(source._format, norichroot) and
3088
 
                    isinstance(target._format, richroot))
3089
 
        except AttributeError:
3090
 
            return False
3091
 
 
3092
 
    @needs_write_lock
3093
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3094
 
        """See InterRepository.fetch()."""
3095
 
        from bzrlib.fetch import Knit1to2Fetcher
3096
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
3097
 
               self.source, self.source._format, self.target, 
3098
 
               self.target._format)
3099
 
        f = Knit1to2Fetcher(to_repository=self.target,
3100
 
                            from_repository=self.source,
3101
 
                            last_revision=revision_id,
3102
 
                            pb=pb, find_ghosts=find_ghosts)
3103
 
        return f.count_copied, f.failed_revisions
3104
 
 
3105
 
 
3106
3036
class InterDifferingSerializer(InterKnitRepo):
3107
3037
 
3108
3038
    @classmethod
3121
3051
            return False
3122
3052
        return True
3123
3053
 
3124
 
    def _fetch_batch(self, revision_ids, basis_id, basis_tree):
 
3054
    def _get_delta_for_revision(self, tree, parent_ids, basis_id, cache):
 
3055
        """Get the best delta and base for this revision.
 
3056
 
 
3057
        :return: (basis_id, delta)
 
3058
        """
 
3059
        possible_trees = [(parent_id, cache[parent_id])
 
3060
                          for parent_id in parent_ids
 
3061
                           if parent_id in cache]
 
3062
        if len(possible_trees) == 0:
 
3063
            # There either aren't any parents, or the parents aren't in the
 
3064
            # cache, so just use the last converted tree
 
3065
            possible_trees.append((basis_id, cache[basis_id]))
 
3066
        deltas = []
 
3067
        for basis_id, basis_tree in possible_trees:
 
3068
            delta = tree.inventory._make_delta(basis_tree.inventory)
 
3069
            deltas.append((len(delta), basis_id, delta))
 
3070
        deltas.sort()
 
3071
        return deltas[0][1:]
 
3072
 
 
3073
    def _fetch_batch(self, revision_ids, basis_id, cache):
3125
3074
        """Fetch across a few revisions.
3126
3075
 
3127
3076
        :param revision_ids: The revisions to copy
3128
 
        :param basis_id: The revision_id of basis_tree
3129
 
        :param basis_tree: A tree that is not in revision_ids which should
3130
 
            already exist in the target.
3131
 
        :return: (basis_id, basis_tree) A new basis to use now that these trees
3132
 
            have been copied.
 
3077
        :param basis_id: The revision_id of a tree that must be in cache, used
 
3078
            as a basis for delta when no other base is available
 
3079
        :param cache: A cache of RevisionTrees that we can use.
 
3080
        :return: The revision_id of the last converted tree. The RevisionTree
 
3081
            for it will be in cache
3133
3082
        """
3134
3083
        # Walk though all revisions; get inventory deltas, copy referenced
3135
3084
        # texts that delta references, insert the delta, revision and
3137
3086
        text_keys = set()
3138
3087
        pending_deltas = []
3139
3088
        pending_revisions = []
 
3089
        parent_map = self.source.get_parent_map(revision_ids)
3140
3090
        for tree in self.source.revision_trees(revision_ids):
3141
3091
            current_revision_id = tree.get_revision_id()
3142
 
            delta = tree.inventory._make_delta(basis_tree.inventory)
 
3092
            parent_ids = parent_map.get(current_revision_id, ())
 
3093
            basis_id, delta = self._get_delta_for_revision(tree, parent_ids,
 
3094
                                                           basis_id, cache)
 
3095
            # Find text entries that need to be copied
3143
3096
            for old_path, new_path, file_id, entry in delta:
3144
3097
                if new_path is not None:
3145
3098
                    if not (new_path or self.target.supports_rich_root()):
3146
 
                        # We leave the inventory delta in, because that
3147
 
                        # will have the deserialised inventory root
3148
 
                        # pointer.
 
3099
                        # We don't copy the text for the root node unless the
 
3100
                        # target supports_rich_root.
3149
3101
                        continue
3150
 
                    # TODO: Do we need:
3151
 
                    #       "if entry.revision == current_revision_id" ?
3152
 
                    if entry.revision == current_revision_id:
3153
 
                        text_keys.add((file_id, entry.revision))
 
3102
                    text_keys.add((file_id, entry.revision))
3154
3103
            revision = self.source.get_revision(current_revision_id)
3155
3104
            pending_deltas.append((basis_id, delta,
3156
3105
                current_revision_id, revision.parent_ids))
3157
3106
            pending_revisions.append(revision)
 
3107
            cache[current_revision_id] = tree
3158
3108
            basis_id = current_revision_id
3159
 
            basis_tree = tree
3160
3109
        # Copy file texts
3161
3110
        from_texts = self.source.texts
3162
3111
        to_texts = self.target.texts
3163
3112
        to_texts.insert_record_stream(from_texts.get_record_stream(
3164
 
            text_keys, self.target._fetch_order,
3165
 
            not self.target._fetch_uses_deltas))
 
3113
            text_keys, self.target._format._fetch_order,
 
3114
            not self.target._format._fetch_uses_deltas))
3166
3115
        # insert deltas
3167
3116
        for delta in pending_deltas:
3168
3117
            self.target.add_inventory_by_delta(*delta)
3176
3125
            except errors.NoSuchRevision:
3177
3126
                pass
3178
3127
            self.target.add_revision(revision.revision_id, revision)
3179
 
        return basis_id, basis_tree
 
3128
        return basis_id
3180
3129
 
3181
3130
    def _fetch_all_revisions(self, revision_ids, pb):
3182
3131
        """Fetch everything for the list of revisions.
3188
3137
        """
3189
3138
        basis_id, basis_tree = self._get_basis(revision_ids[0])
3190
3139
        batch_size = 100
 
3140
        cache = lru_cache.LRUCache(100)
 
3141
        cache[basis_id] = basis_tree
 
3142
        del basis_tree # We don't want to hang on to it here
3191
3143
        for offset in range(0, len(revision_ids), batch_size):
3192
3144
            self.target.start_write_group()
3193
3145
            try:
3194
3146
                pb.update('Transferring revisions', offset,
3195
3147
                          len(revision_ids))
3196
3148
                batch = revision_ids[offset:offset+batch_size]
3197
 
                basis_id, basis_tree = self._fetch_batch(batch,
3198
 
                    basis_id, basis_tree)
 
3149
                basis_id = self._fetch_batch(batch, basis_id, cache)
3199
3150
            except:
3200
3151
                self.target.abort_write_group()
3201
3152
                raise
3205
3156
                  len(revision_ids))
3206
3157
 
3207
3158
    @needs_write_lock
3208
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
 
3159
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
 
3160
            fetch_spec=None):
3209
3161
        """See InterRepository.fetch()."""
 
3162
        if fetch_spec is not None:
 
3163
            raise AssertionError("Not implemented yet...")
3210
3164
        revision_ids = self.target.search_missing_revision_ids(self.source,
3211
3165
            revision_id, find_ghosts=find_ghosts).get_keys()
3212
3166
        if not revision_ids:
3217
3171
            my_pb = ui.ui_factory.nested_progress_bar()
3218
3172
            pb = my_pb
3219
3173
        else:
 
3174
            symbol_versioning.warn(
 
3175
                symbol_versioning.deprecated_in((1, 14, 0))
 
3176
                % "pb parameter to fetch()")
3220
3177
            my_pb = None
3221
3178
        try:
3222
3179
            self._fetch_all_revisions(revision_ids, pb)
3273
3230
            # Make _real_inter use the RemoteRepository for get_parent_map
3274
3231
            self._real_inter.target_get_graph = self.target.get_graph
3275
3232
            self._real_inter.target_get_parent_map = self.target.get_parent_map
3276
 
    
 
3233
 
3277
3234
    def copy_content(self, revision_id=None):
3278
3235
        self._ensure_real_inter()
3279
3236
        self._real_inter.copy_content(revision_id=revision_id)
3280
3237
 
3281
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
 
3238
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
 
3239
            fetch_spec=None):
3282
3240
        self._ensure_real_inter()
3283
3241
        return self._real_inter.fetch(revision_id=revision_id, pb=pb,
3284
 
            find_ghosts=find_ghosts)
 
3242
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
3285
3243
 
3286
3244
    @classmethod
3287
3245
    def _get_repo_format_to_test(self):
3298
3256
    def is_compatible(source, target):
3299
3257
        if not isinstance(source, remote.RemoteRepository):
3300
3258
            return False
3301
 
        # Is source's model compatible with target's model?
3302
 
        source._ensure_real()
3303
 
        real_source = source._real_repository
3304
 
        if isinstance(real_source, remote.RemoteRepository):
3305
 
            raise NotImplementedError(
3306
 
                "We don't support remote repos backed by remote repos yet.")
3307
 
        return InterRepository._same_model(real_source, target)
 
3259
        return InterRepository._same_model(source, target)
3308
3260
 
3309
3261
    def _ensure_real_inter(self):
3310
3262
        if self._real_inter is None:
3311
3263
            self.source._ensure_real()
3312
3264
            real_source = self.source._real_repository
3313
3265
            self._real_inter = InterRepository.get(real_source, self.target)
3314
 
    
3315
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3316
 
        self._ensure_real_inter()
3317
 
        return self._real_inter.fetch(revision_id=revision_id, pb=pb,
3318
 
            find_ghosts=find_ghosts)
3319
3266
 
3320
3267
    def copy_content(self, revision_id=None):
3321
3268
        self._ensure_real_inter()
3342
3289
        from bzrlib.repofmt.pack_repo import RepositoryFormatPack
3343
3290
        if isinstance(source._format, RepositoryFormatPack):
3344
3291
            if isinstance(target, remote.RemoteRepository):
3345
 
                target._ensure_real()
3346
 
                if isinstance(target._real_repository._format,
 
3292
                target._format._ensure_real()
 
3293
                if isinstance(target._format._custom_format,
3347
3294
                              RepositoryFormatPack):
3348
3295
                    if InterRepository._same_model(source, target):
3349
3296
                        return True
3350
3297
        return False
3351
 
    
 
3298
 
3352
3299
    def _autopack(self):
3353
3300
        self.target.autopack()
3354
 
        
 
3301
 
 
3302
    @needs_write_lock
 
3303
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
 
3304
            fetch_spec=None):
 
3305
        """See InterRepository.fetch()."""
 
3306
        if self.target._client._medium._is_remote_before((1, 13)):
 
3307
            # The server won't support the insert_stream RPC, so just use
 
3308
            # regular InterPackRepo logic.  This avoids a bug that causes many
 
3309
            # round-trips for small append calls.
 
3310
            return InterPackRepo.fetch(self, revision_id=revision_id, pb=pb,
 
3311
                find_ghosts=find_ghosts, fetch_spec=fetch_spec)
 
3312
        # Always fetch using the generic streaming fetch code, to allow
 
3313
        # streaming fetching into remote servers.
 
3314
        from bzrlib.fetch import RepoFetcher
 
3315
        fetcher = RepoFetcher(self.target, self.source, revision_id,
 
3316
                              pb, find_ghosts, fetch_spec=fetch_spec)
 
3317
 
3355
3318
    def _get_target_pack_collection(self):
3356
3319
        return self.target._real_repository._pack_collection
3357
3320
 
3364
3327
InterRepository.register_optimiser(InterSameDataRepository)
3365
3328
InterRepository.register_optimiser(InterWeaveRepo)
3366
3329
InterRepository.register_optimiser(InterKnitRepo)
3367
 
InterRepository.register_optimiser(InterModel1and2)
3368
 
InterRepository.register_optimiser(InterKnit1and2)
3369
3330
InterRepository.register_optimiser(InterPackRepo)
3370
3331
InterRepository.register_optimiser(InterOtherToRemote)
3371
3332
InterRepository.register_optimiser(InterRemoteToOther)
3374
3335
 
3375
3336
class CopyConverter(object):
3376
3337
    """A repository conversion tool which just performs a copy of the content.
3377
 
    
 
3338
 
3378
3339
    This is slow but quite reliable.
3379
3340
    """
3380
3341
 
3384
3345
        :param target_format: The format the resulting repository should be.
3385
3346
        """
3386
3347
        self.target_format = target_format
3387
 
        
 
3348
 
3388
3349
    def convert(self, repo, pb):
3389
3350
        """Perform the conversion of to_convert, giving feedback via pb.
3390
3351
 
3459
3420
    def __init__(self, repository):
3460
3421
        self.repository = repository
3461
3422
        self.text_index = self.repository._generate_text_key_index()
3462
 
    
 
3423
 
3463
3424
    def calculate_file_version_parents(self, text_key):
3464
3425
        """Calculate the correct parents for a file version according to
3465
3426
        the inventories.
3526
3487
        revision_graph[key] = tuple(parent for parent in parents if parent
3527
3488
            in revision_graph)
3528
3489
    return revision_graph
 
3490
 
 
3491
 
 
3492
class StreamSink(object):
 
3493
    """An object that can insert a stream into a repository.
 
3494
 
 
3495
    This interface handles the complexity of reserialising inventories and
 
3496
    revisions from different formats, and allows unidirectional insertion into
 
3497
    stacked repositories without looking for the missing basis parents
 
3498
    beforehand.
 
3499
    """
 
3500
 
 
3501
    def __init__(self, target_repo):
 
3502
        self.target_repo = target_repo
 
3503
 
 
3504
    def insert_stream(self, stream, src_format, resume_tokens):
 
3505
        """Insert a stream's content into the target repository.
 
3506
 
 
3507
        :param src_format: a bzr repository format.
 
3508
 
 
3509
        :return: a list of resume tokens and an  iterable of keys additional
 
3510
            items required before the insertion can be completed.
 
3511
        """
 
3512
        self.target_repo.lock_write()
 
3513
        try:
 
3514
            if resume_tokens:
 
3515
                self.target_repo.resume_write_group(resume_tokens)
 
3516
            else:
 
3517
                self.target_repo.start_write_group()
 
3518
            try:
 
3519
                # locked_insert_stream performs a commit|suspend.
 
3520
                return self._locked_insert_stream(stream, src_format)
 
3521
            except:
 
3522
                self.target_repo.abort_write_group(suppress_errors=True)
 
3523
                raise
 
3524
        finally:
 
3525
            self.target_repo.unlock()
 
3526
 
 
3527
    def _locked_insert_stream(self, stream, src_format):
 
3528
        to_serializer = self.target_repo._format._serializer
 
3529
        src_serializer = src_format._serializer
 
3530
        for substream_type, substream in stream:
 
3531
            if substream_type == 'texts':
 
3532
                self.target_repo.texts.insert_record_stream(substream)
 
3533
            elif substream_type == 'inventories':
 
3534
                if src_serializer == to_serializer:
 
3535
                    self.target_repo.inventories.insert_record_stream(
 
3536
                        substream)
 
3537
                else:
 
3538
                    self._extract_and_insert_inventories(
 
3539
                        substream, src_serializer)
 
3540
            elif substream_type == 'revisions':
 
3541
                # This may fallback to extract-and-insert more often than
 
3542
                # required if the serializers are different only in terms of
 
3543
                # the inventory.
 
3544
                if src_serializer == to_serializer:
 
3545
                    self.target_repo.revisions.insert_record_stream(
 
3546
                        substream)
 
3547
                else:
 
3548
                    self._extract_and_insert_revisions(substream,
 
3549
                        src_serializer)
 
3550
            elif substream_type == 'signatures':
 
3551
                self.target_repo.signatures.insert_record_stream(substream)
 
3552
            else:
 
3553
                raise AssertionError('kaboom! %s' % (substream_type,))
 
3554
        try:
 
3555
            missing_keys = set()
 
3556
            for prefix, versioned_file in (
 
3557
                ('texts', self.target_repo.texts),
 
3558
                ('inventories', self.target_repo.inventories),
 
3559
                ('revisions', self.target_repo.revisions),
 
3560
                ('signatures', self.target_repo.signatures),
 
3561
                ):
 
3562
                missing_keys.update((prefix,) + key for key in
 
3563
                    versioned_file.get_missing_compression_parent_keys())
 
3564
        except NotImplementedError:
 
3565
            # cannot even attempt suspending, and missing would have failed
 
3566
            # during stream insertion.
 
3567
            missing_keys = set()
 
3568
        else:
 
3569
            if missing_keys:
 
3570
                # suspend the write group and tell the caller what we is
 
3571
                # missing. We know we can suspend or else we would not have
 
3572
                # entered this code path. (All repositories that can handle
 
3573
                # missing keys can handle suspending a write group).
 
3574
                write_group_tokens = self.target_repo.suspend_write_group()
 
3575
                return write_group_tokens, missing_keys
 
3576
        self.target_repo.commit_write_group()
 
3577
        return [], set()
 
3578
 
 
3579
    def _extract_and_insert_inventories(self, substream, serializer):
 
3580
        """Generate a new inventory versionedfile in target, converting data.
 
3581
 
 
3582
        The inventory is retrieved from the source, (deserializing it), and
 
3583
        stored in the target (reserializing it in a different format).
 
3584
        """
 
3585
        for record in substream:
 
3586
            bytes = record.get_bytes_as('fulltext')
 
3587
            revision_id = record.key[0]
 
3588
            inv = serializer.read_inventory_from_string(bytes, revision_id)
 
3589
            parents = [key[0] for key in record.parents]
 
3590
            self.target_repo.add_inventory(revision_id, inv, parents)
 
3591
 
 
3592
    def _extract_and_insert_revisions(self, substream, serializer):
 
3593
        for record in substream:
 
3594
            bytes = record.get_bytes_as('fulltext')
 
3595
            revision_id = record.key[0]
 
3596
            rev = serializer.read_revision_from_string(bytes)
 
3597
            if rev.revision_id != revision_id:
 
3598
                raise AssertionError('wtf: %s != %s' % (rev, revision_id))
 
3599
            self.target_repo.add_revision(revision_id, rev)
 
3600
 
 
3601
    def finished(self):
 
3602
        if self.target_repo._format._fetch_reconcile:
 
3603
            self.target_repo.reconcile()
 
3604
 
 
3605
 
 
3606
class StreamSource(object):
 
3607
    """A source of a stream for fetching between repositories."""
 
3608
 
 
3609
    def __init__(self, from_repository, to_format):
 
3610
        """Create a StreamSource streaming from from_repository."""
 
3611
        self.from_repository = from_repository
 
3612
        self.to_format = to_format
 
3613
 
 
3614
    def delta_on_metadata(self):
 
3615
        """Return True if delta's are permitted on metadata streams.
 
3616
 
 
3617
        That is on revisions and signatures.
 
3618
        """
 
3619
        src_serializer = self.from_repository._format._serializer
 
3620
        target_serializer = self.to_format._serializer
 
3621
        return (self.to_format._fetch_uses_deltas and
 
3622
            src_serializer == target_serializer)
 
3623
 
 
3624
    def _fetch_revision_texts(self, revs):
 
3625
        # fetch signatures first and then the revision texts
 
3626
        # may need to be a InterRevisionStore call here.
 
3627
        from_sf = self.from_repository.signatures
 
3628
        # A missing signature is just skipped.
 
3629
        keys = [(rev_id,) for rev_id in revs]
 
3630
        signatures = versionedfile.filter_absent(from_sf.get_record_stream(
 
3631
            keys,
 
3632
            self.to_format._fetch_order,
 
3633
            not self.to_format._fetch_uses_deltas))
 
3634
        # If a revision has a delta, this is actually expanded inside the
 
3635
        # insert_record_stream code now, which is an alternate fix for
 
3636
        # bug #261339
 
3637
        from_rf = self.from_repository.revisions
 
3638
        revisions = from_rf.get_record_stream(
 
3639
            keys,
 
3640
            self.to_format._fetch_order,
 
3641
            not self.delta_on_metadata())
 
3642
        return [('signatures', signatures), ('revisions', revisions)]
 
3643
 
 
3644
    def _generate_root_texts(self, revs):
 
3645
        """This will be called by __fetch between fetching weave texts and
 
3646
        fetching the inventory weave.
 
3647
 
 
3648
        Subclasses should override this if they need to generate root texts
 
3649
        after fetching weave texts.
 
3650
        """
 
3651
        if self._rich_root_upgrade():
 
3652
            import bzrlib.fetch
 
3653
            return bzrlib.fetch.Inter1and2Helper(
 
3654
                self.from_repository).generate_root_texts(revs)
 
3655
        else:
 
3656
            return []
 
3657
 
 
3658
    def get_stream(self, search):
 
3659
        phase = 'file'
 
3660
        revs = search.get_keys()
 
3661
        graph = self.from_repository.get_graph()
 
3662
        revs = list(graph.iter_topo_order(revs))
 
3663
        data_to_fetch = self.from_repository.item_keys_introduced_by(revs)
 
3664
        text_keys = []
 
3665
        for knit_kind, file_id, revisions in data_to_fetch:
 
3666
            if knit_kind != phase:
 
3667
                phase = knit_kind
 
3668
                # Make a new progress bar for this phase
 
3669
            if knit_kind == "file":
 
3670
                # Accumulate file texts
 
3671
                text_keys.extend([(file_id, revision) for revision in
 
3672
                    revisions])
 
3673
            elif knit_kind == "inventory":
 
3674
                # Now copy the file texts.
 
3675
                from_texts = self.from_repository.texts
 
3676
                yield ('texts', from_texts.get_record_stream(
 
3677
                    text_keys, self.to_format._fetch_order,
 
3678
                    not self.to_format._fetch_uses_deltas))
 
3679
                # Cause an error if a text occurs after we have done the
 
3680
                # copy.
 
3681
                text_keys = None
 
3682
                # Before we process the inventory we generate the root
 
3683
                # texts (if necessary) so that the inventories references
 
3684
                # will be valid.
 
3685
                for _ in self._generate_root_texts(revs):
 
3686
                    yield _
 
3687
                # NB: This currently reopens the inventory weave in source;
 
3688
                # using a single stream interface instead would avoid this.
 
3689
                from_weave = self.from_repository.inventories
 
3690
                # we fetch only the referenced inventories because we do not
 
3691
                # know for unselected inventories whether all their required
 
3692
                # texts are present in the other repository - it could be
 
3693
                # corrupt.
 
3694
                yield ('inventories', from_weave.get_record_stream(
 
3695
                    [(rev_id,) for rev_id in revs],
 
3696
                    self.inventory_fetch_order(),
 
3697
                    not self.delta_on_metadata()))
 
3698
            elif knit_kind == "signatures":
 
3699
                # Nothing to do here; this will be taken care of when
 
3700
                # _fetch_revision_texts happens.
 
3701
                pass
 
3702
            elif knit_kind == "revisions":
 
3703
                for record in self._fetch_revision_texts(revs):
 
3704
                    yield record
 
3705
            else:
 
3706
                raise AssertionError("Unknown knit kind %r" % knit_kind)
 
3707
 
 
3708
    def get_stream_for_missing_keys(self, missing_keys):
 
3709
        # missing keys can only occur when we are byte copying and not
 
3710
        # translating (because translation means we don't send
 
3711
        # unreconstructable deltas ever).
 
3712
        keys = {}
 
3713
        keys['texts'] = set()
 
3714
        keys['revisions'] = set()
 
3715
        keys['inventories'] = set()
 
3716
        keys['signatures'] = set()
 
3717
        for key in missing_keys:
 
3718
            keys[key[0]].add(key[1:])
 
3719
        if len(keys['revisions']):
 
3720
            # If we allowed copying revisions at this point, we could end up
 
3721
            # copying a revision without copying its required texts: a
 
3722
            # violation of the requirements for repository integrity.
 
3723
            raise AssertionError(
 
3724
                'cannot copy revisions to fill in missing deltas %s' % (
 
3725
                    keys['revisions'],))
 
3726
        for substream_kind, keys in keys.iteritems():
 
3727
            vf = getattr(self.from_repository, substream_kind)
 
3728
            # Ask for full texts always so that we don't need more round trips
 
3729
            # after this stream.
 
3730
            stream = vf.get_record_stream(keys,
 
3731
                self.to_format._fetch_order, True)
 
3732
            yield substream_kind, stream
 
3733
 
 
3734
    def inventory_fetch_order(self):
 
3735
        if self._rich_root_upgrade():
 
3736
            return 'topological'
 
3737
        else:
 
3738
            return self.to_format._fetch_order
 
3739
 
 
3740
    def _rich_root_upgrade(self):
 
3741
        return (not self.from_repository._format.rich_root_data and
 
3742
            self.to_format.rich_root_data)
 
3743