/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-03 03:01:49 UTC
  • mfrom: (4070 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4073.
  • Revision ID: mbp@sourcefrog.net-20090303030149-8p8o8hszdtqa7w8f
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
        one_one,
199
199
 
200
200
    def _generate_revision_if_needed(self):
201
201
        """Create a revision id if None was supplied.
202
 
        
 
202
 
203
203
        If the repository can not support user-specified revision ids
204
204
        they should override this function and raise CannotSetRevisionId
205
205
        if _new_revision_id is not None.
301
301
        :param parent_invs: The inventories of the parent revisions of the
302
302
            commit.
303
303
        :param path: The path the entry is at in the tree.
304
 
        :param tree: The tree which contains this entry and should be used to 
 
304
        :param tree: The tree which contains this entry and should be used to
305
305
            obtain content.
306
306
        :param content_summary: Summary data from the tree about the paths
307
307
            content - stat, length, exec, sha/link target. This is only
514
514
 
515
515
class RootCommitBuilder(CommitBuilder):
516
516
    """This commitbuilder actually records the root id"""
517
 
    
 
517
 
518
518
    # the root entry gets versioned properly by this builder.
519
519
    _versioned_root = True
520
520
 
613
613
 
614
614
    def _abort_write_group(self):
615
615
        """Template method for per-repository write group cleanup.
616
 
        
617
 
        This is called during abort before the write group is considered to be 
 
616
 
 
617
        This is called during abort before the write group is considered to be
618
618
        finished and should cleanup any internal state accrued during the write
619
619
        group. There is no requirement that data handed to the repository be
620
620
        *not* made available - this is not a rollback - but neither should any
626
626
 
627
627
    def add_fallback_repository(self, repository):
628
628
        """Add a repository to use for looking up data not held locally.
629
 
        
 
629
 
630
630
        :param repository: A repository.
631
631
        """
632
632
        if not self._format.supports_external_lookups:
637
637
        self.inventories.add_fallback_versioned_files(repository.inventories)
638
638
        self.revisions.add_fallback_versioned_files(repository.revisions)
639
639
        self.signatures.add_fallback_versioned_files(repository.signatures)
640
 
        self._fetch_order = 'topological'
641
640
 
642
641
    def _check_fallback_repository(self, repository):
643
642
        """Check that this repository can fallback to repository safely.
644
643
 
645
644
        Raise an error if not.
646
 
        
 
645
 
647
646
        :param repository: A repository to fallback to.
648
647
        """
649
648
        return InterRepository._assert_same_model(self, repository)
650
649
 
651
650
    def add_inventory(self, revision_id, inv, parents):
652
651
        """Add the inventory inv to the repository as revision_id.
653
 
        
 
652
 
654
653
        :param parents: The revision ids of the parents that revision_id
655
654
                        is known to have and are in the repository already.
656
655
 
758
757
        self.revisions.add_lines(key, parents, osutils.split_lines(text))
759
758
 
760
759
    def all_revision_ids(self):
761
 
        """Returns a list of all the revision ids in the repository. 
 
760
        """Returns a list of all the revision ids in the repository.
762
761
 
763
762
        This is conceptually deprecated because code should generally work on
764
763
        the graph reachable from a particular revision, and ignore any other
770
769
        return self._all_revision_ids()
771
770
 
772
771
    def _all_revision_ids(self):
773
 
        """Returns a list of all the revision ids in the repository. 
 
772
        """Returns a list of all the revision ids in the repository.
774
773
 
775
 
        These are in as much topological order as the underlying store can 
 
774
        These are in as much topological order as the underlying store can
776
775
        present.
777
776
        """
778
777
        raise NotImplementedError(self._all_revision_ids)
823
822
        self._reconcile_does_inventory_gc = True
824
823
        self._reconcile_fixes_text_parents = False
825
824
        self._reconcile_backsup_inventory = True
826
 
        # not right yet - should be more semantically clear ? 
827
 
        # 
 
825
        # not right yet - should be more semantically clear ?
 
826
        #
828
827
        # TODO: make sure to construct the right store classes, etc, depending
829
828
        # on whether escaping is required.
830
829
        self._warn_if_deprecated()
831
830
        self._write_group = None
832
831
        # Additional places to query for data.
833
832
        self._fallback_repositories = []
834
 
        # What order should fetch operations request streams in?
835
 
        # The default is unordered as that is the cheapest for an origin to
836
 
        # provide.
837
 
        self._fetch_order = 'unordered'
838
 
        # Does this repository use deltas that can be fetched as-deltas ?
839
 
        # (E.g. knits, where the knit deltas can be transplanted intact.
840
 
        # We default to False, which will ensure that enough data to get
841
 
        # a full text out of any fetch stream will be grabbed.
842
 
        self._fetch_uses_deltas = False
843
 
        # Should fetch trigger a reconcile after the fetch? Only needed for
844
 
        # some repository formats that can suffer internal inconsistencies.
845
 
        self._fetch_reconcile = False
846
833
        # An InventoryEntry cache, used during deserialization
847
834
        self._inventory_entry_cache = fifo_cache.FIFOCache(10*1024)
848
835
 
881
868
        This causes caching within the repository obejct to start accumlating
882
869
        data during reads, and allows a 'write_group' to be obtained. Write
883
870
        groups must be used for actual data insertion.
884
 
        
 
871
 
885
872
        :param token: if this is already locked, then lock_write will fail
886
873
            unless the token matches the existing lock.
887
874
        :returns: a token if this instance supports tokens, otherwise None.
916
903
    def leave_lock_in_place(self):
917
904
        """Tell this repository not to release the physical lock when this
918
905
        object is unlocked.
919
 
        
 
906
 
920
907
        If lock_write doesn't return a token, then this method is not supported.
921
908
        """
922
909
        self.control_files.leave_in_place()
1028
1015
    @needs_read_lock
1029
1016
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1030
1017
        """Return the revision ids that other has that this does not.
1031
 
        
 
1018
 
1032
1019
        These are returned in topological order.
1033
1020
 
1034
1021
        revision_id: only return revision ids included by revision_id.
1040
1027
    @needs_read_lock
1041
1028
    def missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1042
1029
        """Return the revision ids that other has that this does not.
1043
 
        
 
1030
 
1044
1031
        These are returned in topological order.
1045
1032
 
1046
1033
        revision_id: only return revision ids included by revision_id.
1066
1053
 
1067
1054
    def copy_content_into(self, destination, revision_id=None):
1068
1055
        """Make a complete copy of the content in self into destination.
1069
 
        
1070
 
        This is a destructive operation! Do not use it on existing 
 
1056
 
 
1057
        This is a destructive operation! Do not use it on existing
1071
1058
        repositories.
1072
1059
        """
1073
1060
        return InterRepository.get(self, destination).copy_content(revision_id)
1087
1074
 
1088
1075
    def _commit_write_group(self):
1089
1076
        """Template method for per-repository write group cleanup.
1090
 
        
1091
 
        This is called before the write group is considered to be 
 
1077
 
 
1078
        This is called before the write group is considered to be
1092
1079
        finished and should ensure that all data handed to the repository
1093
 
        for writing during the write group is safely committed (to the 
 
1080
        for writing during the write group is safely committed (to the
1094
1081
        extent possible considering file system caching etc).
1095
1082
        """
1096
1083
 
 
1084
    def suspend_write_group(self):
 
1085
        raise errors.UnsuspendableWriteGroup(self)
 
1086
 
 
1087
    def resume_write_group(self, tokens):
 
1088
        if not self.is_write_locked():
 
1089
            raise errors.NotWriteLocked(self)
 
1090
        if self._write_group:
 
1091
            raise errors.BzrError('already in a write group')
 
1092
        self._resume_write_group(tokens)
 
1093
        # so we can detect unlock/relock - the write group is now entered.
 
1094
        self._write_group = self.get_transaction()
 
1095
 
 
1096
    def _resume_write_group(self, tokens):
 
1097
        raise errors.UnsuspendableWriteGroup(self)
 
1098
 
1097
1099
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False):
1098
1100
        """Fetch the content required to construct revision_id from source.
1099
1101
 
1124
1126
                           timezone=None, committer=None, revprops=None,
1125
1127
                           revision_id=None):
1126
1128
        """Obtain a CommitBuilder for this repository.
1127
 
        
 
1129
 
1128
1130
        :param branch: Branch to commit to.
1129
1131
        :param parents: Revision ids of the parents of the new revision.
1130
1132
        :param config: Configuration to use.
1192
1194
 
1193
1195
    def _start_write_group(self):
1194
1196
        """Template method for per-repository write group startup.
1195
 
        
1196
 
        This is called before the write group is considered to be 
 
1197
 
 
1198
        This is called before the write group is considered to be
1197
1199
        entered.
1198
1200
        """
1199
1201
 
1220
1222
                dest_repo = a_bzrdir.open_repository()
1221
1223
        return dest_repo
1222
1224
 
 
1225
    def _get_sink(self):
 
1226
        """Return a sink for streaming into this repository."""
 
1227
        return StreamSink(self)
 
1228
 
 
1229
    def _get_source(self, to_format):
 
1230
        """Return a source for streaming from this repository."""
 
1231
        return StreamSource(self, to_format)
 
1232
 
1223
1233
    @needs_read_lock
1224
1234
    def has_revision(self, revision_id):
1225
1235
        """True if this repository has a copy of the revision."""
1248
1258
    @needs_read_lock
1249
1259
    def get_revision_reconcile(self, revision_id):
1250
1260
        """'reconcile' helper routine that allows access to a revision always.
1251
 
        
 
1261
 
1252
1262
        This variant of get_revision does not cross check the weave graph
1253
1263
        against the revision one as get_revision does: but it should only
1254
1264
        be used by reconcile, or reconcile-alike commands that are correcting
1292
1302
 
1293
1303
    def get_deltas_for_revisions(self, revisions):
1294
1304
        """Produce a generator of revision deltas.
1295
 
        
 
1305
 
1296
1306
        Note that the input is a sequence of REVISIONS, not revision_ids.
1297
1307
        Trees will be held in memory until the generator exits.
1298
1308
        Each delta is relative to the revision's lefthand predecessor.
1301
1311
        for revision in revisions:
1302
1312
            required_trees.add(revision.revision_id)
1303
1313
            required_trees.update(revision.parent_ids[:1])
1304
 
        trees = dict((t.get_revision_id(), t) for 
 
1314
        trees = dict((t.get_revision_id(), t) for
1305
1315
                     t in self.revision_trees(required_trees))
1306
1316
        for revision in revisions:
1307
1317
            if not revision.parent_ids:
1372
1382
 
1373
1383
        # this code needs to read every new line in every inventory for the
1374
1384
        # inventories [revision_ids]. Seeing a line twice is ok. Seeing a line
1375
 
        # not present in one of those inventories is unnecessary but not 
 
1385
        # not present in one of those inventories is unnecessary but not
1376
1386
        # harmful because we are filtering by the revision id marker in the
1377
 
        # inventory lines : we only select file ids altered in one of those  
 
1387
        # inventory lines : we only select file ids altered in one of those
1378
1388
        # revisions. We don't need to see all lines in the inventory because
1379
1389
        # only those added in an inventory in rev X can contain a revision=X
1380
1390
        # line.
1695
1705
            yield ''.join(chunks), key[-1]
1696
1706
 
1697
1707
    def deserialise_inventory(self, revision_id, xml):
1698
 
        """Transform the xml into an inventory object. 
 
1708
        """Transform the xml into an inventory object.
1699
1709
 
1700
1710
        :param revision_id: The expected revision id of the inventory.
1701
1711
        :param xml: A serialised inventory.
1803
1813
        # TODO: refactor this to use an existing revision object
1804
1814
        # so we don't need to read it in twice.
1805
1815
        if revision_id == _mod_revision.NULL_REVISION:
1806
 
            return RevisionTree(self, Inventory(root_id=None), 
 
1816
            return RevisionTree(self, Inventory(root_id=None),
1807
1817
                                _mod_revision.NULL_REVISION)
1808
1818
        else:
1809
1819
            inv = self.get_revision_inventory(revision_id)
1821
1831
    def get_ancestry(self, revision_id, topo_sorted=True):
1822
1832
        """Return a list of revision-ids integrated by a revision.
1823
1833
 
1824
 
        The first element of the list is always None, indicating the origin 
1825
 
        revision.  This might change when we have history horizons, or 
 
1834
        The first element of the list is always None, indicating the origin
 
1835
        revision.  This might change when we have history horizons, or
1826
1836
        perhaps we should have a new API.
1827
 
        
 
1837
 
1828
1838
        This is topologically sorted.
1829
1839
        """
1830
1840
        if _mod_revision.is_null(revision_id):
1854
1864
        types it should be a no-op that just returns.
1855
1865
 
1856
1866
        This stub method does not require a lock, but subclasses should use
1857
 
        @needs_write_lock as this is a long running call its reasonable to 
 
1867
        @needs_write_lock as this is a long running call its reasonable to
1858
1868
        implicitly lock for the user.
1859
1869
        """
1860
1870
 
1862
1872
    @deprecated_method(one_six)
1863
1873
    def print_file(self, file, revision_id):
1864
1874
        """Print `file` to stdout.
1865
 
        
 
1875
 
1866
1876
        FIXME RBC 20060125 as John Meinel points out this is a bad api
1867
1877
        - it writes to stdout, it assumes that that is valid etc. Fix
1868
1878
        by creating a new more flexible convenience function.
1948
1958
                          working trees.
1949
1959
        """
1950
1960
        raise NotImplementedError(self.set_make_working_trees)
1951
 
    
 
1961
 
1952
1962
    def make_working_trees(self):
1953
1963
        """Returns the policy for making working trees on new branches."""
1954
1964
        raise NotImplementedError(self.make_working_trees)
2019
2029
                    revision_id.decode('ascii')
2020
2030
                except UnicodeDecodeError:
2021
2031
                    raise errors.NonAsciiRevisionId(method, self)
2022
 
    
 
2032
 
2023
2033
    def revision_graph_can_have_wrong_parents(self):
2024
2034
        """Is it possible for this repository to have a revision graph with
2025
2035
        incorrect parents?
2143
2153
 
2144
2154
class MetaDirRepository(Repository):
2145
2155
    """Repositories in the new meta-dir layout.
2146
 
    
 
2156
 
2147
2157
    :ivar _transport: Transport for access to repository control files,
2148
2158
        typically pointing to .bzr/repository.
2149
2159
    """
2174
2184
        else:
2175
2185
            self._transport.put_bytes('no-working-trees', '',
2176
2186
                mode=self.bzrdir._get_file_mode())
2177
 
    
 
2187
 
2178
2188
    def make_working_trees(self):
2179
2189
        """Returns the policy for making working trees on new branches."""
2180
2190
        return not self._transport.has('no-working-trees')
2188
2198
            control_files)
2189
2199
 
2190
2200
 
2191
 
class RepositoryFormatRegistry(registry.Registry):
2192
 
    """Registry of RepositoryFormats."""
2193
 
 
2194
 
    def get(self, format_string):
2195
 
        r = registry.Registry.get(self, format_string)
2196
 
        if callable(r):
2197
 
            r = r()
2198
 
        return r
2199
 
    
2200
 
 
2201
 
format_registry = RepositoryFormatRegistry()
2202
 
"""Registry of formats, indexed by their identifying format string.
 
2201
network_format_registry = registry.FormatRegistry()
 
2202
"""Registry of formats indexed by their network name.
 
2203
 
 
2204
The network name for a repository format is an identifier that can be used when
 
2205
referring to formats with smart server operations. See
 
2206
RepositoryFormat.network_name() for more detail.
 
2207
"""
 
2208
 
 
2209
 
 
2210
format_registry = registry.FormatRegistry(network_format_registry)
 
2211
"""Registry of formats, indexed by their BzrDirMetaFormat format string.
2203
2212
 
2204
2213
This can contain either format instances themselves, or classes/factories that
2205
2214
can be called to obtain one.
2212
2221
class RepositoryFormat(object):
2213
2222
    """A repository format.
2214
2223
 
2215
 
    Formats provide three things:
 
2224
    Formats provide four things:
2216
2225
     * An initialization routine to construct repository data on disk.
2217
 
     * a format string which is used when the BzrDir supports versioned
2218
 
       children.
 
2226
     * a optional format string which is used when the BzrDir supports
 
2227
       versioned children.
2219
2228
     * an open routine which returns a Repository instance.
 
2229
     * A network name for referring to the format in smart server RPC
 
2230
       methods.
2220
2231
 
2221
2232
    There is one and only one Format subclass for each on-disk format. But
2222
2233
    there can be one Repository subclass that is used for several different
2223
2234
    formats. The _format attribute on a Repository instance can be used to
2224
2235
    determine the disk format.
2225
2236
 
2226
 
    Formats are placed in an dict by their format string for reference 
2227
 
    during opening. These should be subclasses of RepositoryFormat
2228
 
    for consistency.
 
2237
    Formats are placed in a registry by their format string for reference
 
2238
    during opening. These should be subclasses of RepositoryFormat for
 
2239
    consistency.
2229
2240
 
2230
2241
    Once a format is deprecated, just deprecate the initialize and open
2231
 
    methods on the format class. Do not deprecate the object, as the 
2232
 
    object will be created every system load.
 
2242
    methods on the format class. Do not deprecate the object, as the
 
2243
    object may be created even when a repository instnace hasn't been
 
2244
    created.
2233
2245
 
2234
2246
    Common instance attributes:
2235
2247
    _matchingbzrdir - the bzrdir format that the repository format was
2244
2256
    # Can this repository be given external locations to lookup additional
2245
2257
    # data. Set to True or False in derived classes.
2246
2258
    supports_external_lookups = None
 
2259
    # What order should fetch operations request streams in?
 
2260
    # The default is unordered as that is the cheapest for an origin to
 
2261
    # provide.
 
2262
    _fetch_order = 'unordered'
 
2263
    # Does this repository format use deltas that can be fetched as-deltas ?
 
2264
    # (E.g. knits, where the knit deltas can be transplanted intact.
 
2265
    # We default to False, which will ensure that enough data to get
 
2266
    # a full text out of any fetch stream will be grabbed.
 
2267
    _fetch_uses_deltas = False
 
2268
    # Should fetch trigger a reconcile after the fetch? Only needed for
 
2269
    # some repository formats that can suffer internal inconsistencies.
 
2270
    _fetch_reconcile = False
2247
2271
 
2248
2272
    def __str__(self):
2249
2273
        return "<%s>" % self.__class__.__name__
2258
2282
    @classmethod
2259
2283
    def find_format(klass, a_bzrdir):
2260
2284
        """Return the format for the repository object in a_bzrdir.
2261
 
        
 
2285
 
2262
2286
        This is used by bzr native formats that have a "format" file in
2263
 
        the repository.  Other methods may be used by different types of 
 
2287
        the repository.  Other methods may be used by different types of
2264
2288
        control directory.
2265
2289
        """
2266
2290
        try:
2280
2304
    @classmethod
2281
2305
    def unregister_format(klass, format):
2282
2306
        format_registry.remove(format.get_format_string())
2283
 
    
 
2307
 
2284
2308
    @classmethod
2285
2309
    def get_default_format(klass):
2286
2310
        """Return the current default format."""
2289
2313
 
2290
2314
    def get_format_string(self):
2291
2315
        """Return the ASCII format string that identifies this format.
2292
 
        
2293
 
        Note that in pre format ?? repositories the format string is 
 
2316
 
 
2317
        Note that in pre format ?? repositories the format string is
2294
2318
        not permitted nor written to disk.
2295
2319
        """
2296
2320
        raise NotImplementedError(self.get_format_string)
2327
2351
        :param a_bzrdir: The bzrdir to put the new repository in it.
2328
2352
        :param shared: The repository should be initialized as a sharable one.
2329
2353
        :returns: The new repository object.
2330
 
        
 
2354
 
2331
2355
        This may raise UninitializableFormat if shared repository are not
2332
2356
        compatible the a_bzrdir.
2333
2357
        """
2337
2361
        """Is this format supported?
2338
2362
 
2339
2363
        Supported formats must be initializable and openable.
2340
 
        Unsupported formats may not support initialization or committing or 
 
2364
        Unsupported formats may not support initialization or committing or
2341
2365
        some other features depending on the reason for not being supported.
2342
2366
        """
2343
2367
        return True
2344
2368
 
 
2369
    def network_name(self):
 
2370
        """A simple byte string uniquely identifying this format for RPC calls.
 
2371
 
 
2372
        MetaDir repository formats use their disk format string to identify the
 
2373
        repository over the wire. All in one formats such as bzr < 0.8, and
 
2374
        foreign formats like svn/git and hg should use some marker which is
 
2375
        unique and immutable.
 
2376
        """
 
2377
        raise NotImplementedError(self.network_name)
 
2378
 
2345
2379
    def check_conversion_target(self, target_format):
2346
2380
        raise NotImplementedError(self.check_conversion_target)
2347
2381
 
2348
2382
    def open(self, a_bzrdir, _found=False):
2349
2383
        """Return an instance of this format for the bzrdir a_bzrdir.
2350
 
        
 
2384
 
2351
2385
        _found is a private parameter, do not use it.
2352
2386
        """
2353
2387
        raise NotImplementedError(self.open)
2397
2431
        finally:
2398
2432
            control_files.unlock()
2399
2433
 
2400
 
 
2401
 
# formats which have no format string are not discoverable
2402
 
# and not independently creatable, so are not registered.  They're 
 
2434
    def network_name(self):
 
2435
        """Metadir formats have matching disk and network format strings."""
 
2436
        return self.get_format_string()
 
2437
 
 
2438
 
 
2439
# Pre-0.8 formats that don't have a disk format string (because they are
 
2440
# versioned by the matching control directory). We use the control directories
 
2441
# disk format string as a key for the network_name because they meet the
 
2442
# constraints (simple string, unique, immmutable).
 
2443
network_format_registry.register_lazy(
 
2444
    "Bazaar-NG branch, format 5\n",
 
2445
    'bzrlib.repofmt.weaverepo',
 
2446
    'RepositoryFormat5',
 
2447
)
 
2448
network_format_registry.register_lazy(
 
2449
    "Bazaar-NG branch, format 6\n",
 
2450
    'bzrlib.repofmt.weaverepo',
 
2451
    'RepositoryFormat6',
 
2452
)
 
2453
 
 
2454
# formats which have no format string are not discoverable or independently
 
2455
# creatable on disk, so are not registered in format_registry.  They're
2403
2456
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
2404
2457
# needed, it's constructed directly by the BzrDir.  Non-native formats where
2405
2458
# the repository is not separately opened are similar.
2472
2525
    'RepositoryFormatKnitPack6RichRoot',
2473
2526
    )
2474
2527
 
2475
 
# Development formats. 
 
2528
# Development formats.
2476
2529
# 1.7->1.8 go below here
2477
2530
format_registry.register_lazy(
2478
2531
    "Bazaar development format 2 (needs bzr.dev from before 1.8)\n",
2491
2544
    """This class represents operations taking place between two repositories.
2492
2545
 
2493
2546
    Its instances have methods like copy_content and fetch, and contain
2494
 
    references to the source and target repositories these operations can be 
 
2547
    references to the source and target repositories these operations can be
2495
2548
    carried out on.
2496
2549
 
2497
2550
    Often we will provide convenience methods on 'repository' which carry out
2510
2563
        self.target_get_graph = self.target.get_graph
2511
2564
        self.target_get_parent_map = self.target.get_parent_map
2512
2565
 
 
2566
    @needs_write_lock
2513
2567
    def copy_content(self, revision_id=None):
2514
 
        raise NotImplementedError(self.copy_content)
 
2568
        """Make a complete copy of the content in self into destination.
 
2569
 
 
2570
        This is a destructive operation! Do not use it on existing
 
2571
        repositories.
 
2572
 
 
2573
        :param revision_id: Only copy the content needed to construct
 
2574
                            revision_id and its parents.
 
2575
        """
 
2576
        try:
 
2577
            self.target.set_make_working_trees(self.source.make_working_trees())
 
2578
        except NotImplementedError:
 
2579
            pass
 
2580
        self.target.fetch(self.source, revision_id=revision_id)
2515
2581
 
2516
2582
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2517
2583
        """Fetch the content required to construct revision_id.
2522
2588
                            content is copied.
2523
2589
        :param pb: optional progress bar to use for progress reports. If not
2524
2590
                   provided a default one will be created.
2525
 
 
2526
 
        :returns: (copied_revision_count, failures).
 
2591
        :return: None.
2527
2592
        """
2528
 
        # Normally we should find a specific InterRepository subclass to do
2529
 
        # the fetch; if nothing else then at least InterSameDataRepository.
2530
 
        # If none of them is suitable it looks like fetching is not possible;
2531
 
        # we try to give a good message why.  _assert_same_model will probably
2532
 
        # give a helpful message; otherwise a generic one.
2533
 
        self._assert_same_model(self.source, self.target)
2534
 
        raise errors.IncompatibleRepositories(self.source, self.target,
2535
 
            "no suitableInterRepository found")
 
2593
        from bzrlib.fetch import RepoFetcher
 
2594
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
 
2595
               self.source, self.source._format, self.target,
 
2596
               self.target._format)
 
2597
        f = RepoFetcher(to_repository=self.target,
 
2598
                               from_repository=self.source,
 
2599
                               last_revision=revision_id,
 
2600
                               pb=pb, find_ghosts=find_ghosts)
2536
2601
 
2537
2602
    def _walk_to_common_revisions(self, revision_ids):
2538
2603
        """Walk out from revision_ids in source to revisions target has.
2597
2662
    @needs_read_lock
2598
2663
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
2599
2664
        """Return the revision ids that source has that target does not.
2600
 
        
 
2665
 
2601
2666
        These are returned in topological order.
2602
2667
 
2603
2668
        :param revision_id: only return revision ids included by this
2611
2676
    @needs_read_lock
2612
2677
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2613
2678
        """Return the revision ids that source has that target does not.
2614
 
        
 
2679
 
2615
2680
        :param revision_id: only return revision ids included by this
2616
2681
                            revision_id.
2617
2682
        :param find_ghosts: If True find missing revisions in deep history
2636
2701
    @staticmethod
2637
2702
    def _same_model(source, target):
2638
2703
        """True if source and target have the same data representation.
2639
 
        
 
2704
 
2640
2705
        Note: this is always called on the base class; overriding it in a
2641
2706
        subclass will have no effect.
2642
2707
        """
2660
2725
 
2661
2726
class InterSameDataRepository(InterRepository):
2662
2727
    """Code for converting between repositories that represent the same data.
2663
 
    
 
2728
 
2664
2729
    Data format and model must match for this to work.
2665
2730
    """
2666
2731
 
2667
2732
    @classmethod
2668
2733
    def _get_repo_format_to_test(self):
2669
2734
        """Repository format for testing with.
2670
 
        
 
2735
 
2671
2736
        InterSameData can pull from subtree to subtree and from non-subtree to
2672
2737
        non-subtree, so we test this with the richest repository format.
2673
2738
        """
2678
2743
    def is_compatible(source, target):
2679
2744
        return InterRepository._same_model(source, target)
2680
2745
 
2681
 
    @needs_write_lock
2682
 
    def copy_content(self, revision_id=None):
2683
 
        """Make a complete copy of the content in self into destination.
2684
 
 
2685
 
        This copies both the repository's revision data, and configuration information
2686
 
        such as the make_working_trees setting.
2687
 
        
2688
 
        This is a destructive operation! Do not use it on existing 
2689
 
        repositories.
2690
 
 
2691
 
        :param revision_id: Only copy the content needed to construct
2692
 
                            revision_id and its parents.
2693
 
        """
2694
 
        try:
2695
 
            self.target.set_make_working_trees(self.source.make_working_trees())
2696
 
        except NotImplementedError:
2697
 
            pass
2698
 
        # but don't bother fetching if we have the needed data now.
2699
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
2700
 
            self.target.has_revision(revision_id)):
2701
 
            return
2702
 
        self.target.fetch(self.source, revision_id=revision_id)
2703
 
 
2704
 
    @needs_write_lock
2705
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
2706
 
        """See InterRepository.fetch()."""
2707
 
        from bzrlib.fetch import RepoFetcher
2708
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2709
 
               self.source, self.source._format, self.target,
2710
 
               self.target._format)
2711
 
        f = RepoFetcher(to_repository=self.target,
2712
 
                               from_repository=self.source,
2713
 
                               last_revision=revision_id,
2714
 
                               pb=pb, find_ghosts=find_ghosts)
2715
 
        return f.count_copied, f.failed_revisions
2716
 
 
2717
2746
 
2718
2747
class InterWeaveRepo(InterSameDataRepository):
2719
2748
    """Optimised code paths between Weave based repositories.
2720
 
    
 
2749
 
2721
2750
    This should be in bzrlib/repofmt/weaverepo.py but we have not yet
2722
2751
    implemented lazy inter-object optimisation.
2723
2752
    """
2730
2759
    @staticmethod
2731
2760
    def is_compatible(source, target):
2732
2761
        """Be compatible with known Weave formats.
2733
 
        
 
2762
 
2734
2763
        We don't test for the stores being of specific types because that
2735
 
        could lead to confusing results, and there is no need to be 
 
2764
        could lead to confusing results, and there is no need to be
2736
2765
        overly general.
2737
2766
        """
2738
2767
        from bzrlib.repofmt.weaverepo import (
2749
2778
                                                RepositoryFormat7)))
2750
2779
        except AttributeError:
2751
2780
            return False
2752
 
    
 
2781
 
2753
2782
    @needs_write_lock
2754
2783
    def copy_content(self, revision_id=None):
2755
2784
        """See InterRepository.copy_content()."""
2792
2821
                               from_repository=self.source,
2793
2822
                               last_revision=revision_id,
2794
2823
                               pb=pb, find_ghosts=find_ghosts)
2795
 
        return f.count_copied, f.failed_revisions
2796
2824
 
2797
2825
    @needs_read_lock
2798
2826
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2799
2827
        """See InterRepository.missing_revision_ids()."""
2800
2828
        # we want all revisions to satisfy revision_id in source.
2801
2829
        # but we don't want to stat every file here and there.
2802
 
        # we want then, all revisions other needs to satisfy revision_id 
 
2830
        # we want then, all revisions other needs to satisfy revision_id
2803
2831
        # checked, but not those that we have locally.
2804
 
        # so the first thing is to get a subset of the revisions to 
 
2832
        # so the first thing is to get a subset of the revisions to
2805
2833
        # satisfy revision_id in source, and then eliminate those that
2806
 
        # we do already have. 
 
2834
        # we do already have.
2807
2835
        # this is slow on high latency connection to self, but as as this
2808
 
        # disk format scales terribly for push anyway due to rewriting 
 
2836
        # disk format scales terribly for push anyway due to rewriting
2809
2837
        # inventory.weave, this is considered acceptable.
2810
2838
        # - RBC 20060209
2811
2839
        if revision_id is not None:
2831
2859
            # and the tip revision was validated by get_ancestry.
2832
2860
            result_set = required_revisions
2833
2861
        else:
2834
 
            # if we just grabbed the possibly available ids, then 
 
2862
            # if we just grabbed the possibly available ids, then
2835
2863
            # we only have an estimate of whats available and need to validate
2836
2864
            # that against the revision records.
2837
2865
            result_set = set(
2850
2878
    @staticmethod
2851
2879
    def is_compatible(source, target):
2852
2880
        """Be compatible with known Knit formats.
2853
 
        
 
2881
 
2854
2882
        We don't test for the stores being of specific types because that
2855
 
        could lead to confusing results, and there is no need to be 
 
2883
        could lead to confusing results, and there is no need to be
2856
2884
        overly general.
2857
2885
        """
2858
2886
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
2873
2901
                            from_repository=self.source,
2874
2902
                            last_revision=revision_id,
2875
2903
                            pb=pb, find_ghosts=find_ghosts)
2876
 
        return f.count_copied, f.failed_revisions
2877
2904
 
2878
2905
    @needs_read_lock
2879
2906
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2901
2928
            # and the tip revision was validated by get_ancestry.
2902
2929
            result_set = required_revisions
2903
2930
        else:
2904
 
            # if we just grabbed the possibly available ids, then 
 
2931
            # if we just grabbed the possibly available ids, then
2905
2932
            # we only have an estimate of whats available and need to validate
2906
2933
            # that against the revision records.
2907
2934
            result_set = set(
2920
2947
    @staticmethod
2921
2948
    def is_compatible(source, target):
2922
2949
        """Be compatible with known Pack formats.
2923
 
        
 
2950
 
2924
2951
        We don't test for the stores being of specific types because that
2925
 
        could lead to confusing results, and there is no need to be 
 
2952
        could lead to confusing results, and there is no need to be
2926
2953
        overly general.
2927
2954
        """
2928
2955
        from bzrlib.repofmt.pack_repo import RepositoryFormatPack
2945
2972
            from bzrlib.fetch import RepoFetcher
2946
2973
            fetcher = RepoFetcher(self.target, self.source, revision_id,
2947
2974
                                  pb, find_ghosts)
2948
 
            return fetcher.count_copied, fetcher.failed_revisions
2949
2975
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
2950
2976
               self.source, self.source._format, self.target, self.target._format)
2951
 
        self.count_copied = 0
2952
2977
        if revision_id is None:
2953
2978
            # TODO:
2954
2979
            # everything to do - use pack logic
3003
3028
 
3004
3029
    def _autopack(self):
3005
3030
        self.target._pack_collection.autopack()
3006
 
        
 
3031
 
3007
3032
    def _get_target_pack_collection(self):
3008
3033
        return self.target._pack_collection
3009
3034
 
3010
3035
    @needs_read_lock
3011
3036
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3012
3037
        """See InterRepository.missing_revision_ids().
3013
 
        
 
3038
 
3014
3039
        :param find_ghosts: Find ghosts throughout the ancestry of
3015
3040
            revision_id.
3016
3041
        """
3047
3072
        return self.source.revision_ids_to_search_result(result_set)
3048
3073
 
3049
3074
 
3050
 
class InterModel1and2(InterRepository):
3051
 
 
3052
 
    @classmethod
3053
 
    def _get_repo_format_to_test(self):
3054
 
        return None
3055
 
 
3056
 
    @staticmethod
3057
 
    def is_compatible(source, target):
3058
 
        if not source.supports_rich_root() and target.supports_rich_root():
3059
 
            return True
3060
 
        else:
3061
 
            return False
3062
 
 
3063
 
    @needs_write_lock
3064
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3065
 
        """See InterRepository.fetch()."""
3066
 
        from bzrlib.fetch import Model1toKnit2Fetcher
3067
 
        f = Model1toKnit2Fetcher(to_repository=self.target,
3068
 
                                 from_repository=self.source,
3069
 
                                 last_revision=revision_id,
3070
 
                                 pb=pb, find_ghosts=find_ghosts)
3071
 
        return f.count_copied, f.failed_revisions
3072
 
 
3073
 
    @needs_write_lock
3074
 
    def copy_content(self, revision_id=None):
3075
 
        """Make a complete copy of the content in self into destination.
3076
 
        
3077
 
        This is a destructive operation! Do not use it on existing 
3078
 
        repositories.
3079
 
 
3080
 
        :param revision_id: Only copy the content needed to construct
3081
 
                            revision_id and its parents.
3082
 
        """
3083
 
        try:
3084
 
            self.target.set_make_working_trees(self.source.make_working_trees())
3085
 
        except NotImplementedError:
3086
 
            pass
3087
 
        # but don't bother fetching if we have the needed data now.
3088
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
3089
 
            self.target.has_revision(revision_id)):
3090
 
            return
3091
 
        self.target.fetch(self.source, revision_id=revision_id)
3092
 
 
3093
 
 
3094
 
class InterKnit1and2(InterKnitRepo):
3095
 
 
3096
 
    @classmethod
3097
 
    def _get_repo_format_to_test(self):
3098
 
        return None
3099
 
 
3100
 
    @staticmethod
3101
 
    def is_compatible(source, target):
3102
 
        """Be compatible with Knit1 source and Knit3 target"""
3103
 
        try:
3104
 
            from bzrlib.repofmt.knitrepo import (
3105
 
                RepositoryFormatKnit1,
3106
 
                RepositoryFormatKnit3,
3107
 
                )
3108
 
            from bzrlib.repofmt.pack_repo import (
3109
 
                RepositoryFormatKnitPack1,
3110
 
                RepositoryFormatKnitPack3,
3111
 
                RepositoryFormatKnitPack4,
3112
 
                RepositoryFormatKnitPack5,
3113
 
                RepositoryFormatKnitPack5RichRoot,
3114
 
                RepositoryFormatKnitPack6,
3115
 
                RepositoryFormatKnitPack6RichRoot,
3116
 
                RepositoryFormatPackDevelopment2,
3117
 
                RepositoryFormatPackDevelopment2Subtree,
3118
 
                )
3119
 
            norichroot = (
3120
 
                RepositoryFormatKnit1,            # no rr, no subtree
3121
 
                RepositoryFormatKnitPack1,        # no rr, no subtree
3122
 
                RepositoryFormatPackDevelopment2, # no rr, no subtree
3123
 
                RepositoryFormatKnitPack5,        # no rr, no subtree
3124
 
                RepositoryFormatKnitPack6,        # no rr, no subtree
3125
 
                )
3126
 
            richroot = (
3127
 
                RepositoryFormatKnit3,            # rr, subtree
3128
 
                RepositoryFormatKnitPack3,        # rr, subtree
3129
 
                RepositoryFormatKnitPack4,        # rr, no subtree
3130
 
                RepositoryFormatKnitPack5RichRoot,# rr, no subtree
3131
 
                RepositoryFormatKnitPack6RichRoot,# rr, no subtree
3132
 
                RepositoryFormatPackDevelopment2Subtree, # rr, subtree
3133
 
                )
3134
 
            for format in norichroot:
3135
 
                if format.rich_root_data:
3136
 
                    raise AssertionError('Format %s is a rich-root format'
3137
 
                        ' but is included in the non-rich-root list'
3138
 
                        % (format,))
3139
 
            for format in richroot:
3140
 
                if not format.rich_root_data:
3141
 
                    raise AssertionError('Format %s is not a rich-root format'
3142
 
                        ' but is included in the rich-root list'
3143
 
                        % (format,))
3144
 
            # TODO: One alternative is to just check format.rich_root_data,
3145
 
            #       instead of keeping membership lists. However, the formats
3146
 
            #       *also* have to use the same 'Knit' style of storage
3147
 
            #       (line-deltas, fulltexts, etc.)
3148
 
            return (isinstance(source._format, norichroot) and
3149
 
                    isinstance(target._format, richroot))
3150
 
        except AttributeError:
3151
 
            return False
3152
 
 
3153
 
    @needs_write_lock
3154
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3155
 
        """See InterRepository.fetch()."""
3156
 
        from bzrlib.fetch import Knit1to2Fetcher
3157
 
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
3158
 
               self.source, self.source._format, self.target, 
3159
 
               self.target._format)
3160
 
        f = Knit1to2Fetcher(to_repository=self.target,
3161
 
                            from_repository=self.source,
3162
 
                            last_revision=revision_id,
3163
 
                            pb=pb, find_ghosts=find_ghosts)
3164
 
        return f.count_copied, f.failed_revisions
3165
 
 
3166
 
 
3167
3075
class InterDifferingSerializer(InterKnitRepo):
3168
3076
 
3169
3077
    @classmethod
3182
3090
            return False
3183
3091
        return True
3184
3092
 
3185
 
    def _fetch_batch(self, revision_ids, basis_id, basis_tree):
 
3093
    def _get_delta_for_revision(self, tree, parent_ids, basis_id, cache):
 
3094
        """Get the best delta and base for this revision.
 
3095
 
 
3096
        :return: (basis_id, delta)
 
3097
        """
 
3098
        possible_trees = [(parent_id, cache[parent_id])
 
3099
                          for parent_id in parent_ids
 
3100
                           if parent_id in cache]
 
3101
        if len(possible_trees) == 0:
 
3102
            # There either aren't any parents, or the parents aren't in the
 
3103
            # cache, so just use the last converted tree
 
3104
            possible_trees.append((basis_id, cache[basis_id]))
 
3105
        deltas = []
 
3106
        for basis_id, basis_tree in possible_trees:
 
3107
            delta = tree.inventory._make_delta(basis_tree.inventory)
 
3108
            deltas.append((len(delta), basis_id, delta))
 
3109
        deltas.sort()
 
3110
        return deltas[0][1:]
 
3111
 
 
3112
    def _fetch_batch(self, revision_ids, basis_id, cache):
3186
3113
        """Fetch across a few revisions.
3187
3114
 
3188
3115
        :param revision_ids: The revisions to copy
3189
 
        :param basis_id: The revision_id of basis_tree
3190
 
        :param basis_tree: A tree that is not in revision_ids which should
3191
 
            already exist in the target.
3192
 
        :return: (basis_id, basis_tree) A new basis to use now that these trees
3193
 
            have been copied.
 
3116
        :param basis_id: The revision_id of a tree that must be in cache, used
 
3117
            as a basis for delta when no other base is available
 
3118
        :param cache: A cache of RevisionTrees that we can use.
 
3119
        :return: The revision_id of the last converted tree. The RevisionTree
 
3120
            for it will be in cache
3194
3121
        """
3195
3122
        # Walk though all revisions; get inventory deltas, copy referenced
3196
3123
        # texts that delta references, insert the delta, revision and
3198
3125
        text_keys = set()
3199
3126
        pending_deltas = []
3200
3127
        pending_revisions = []
 
3128
        parent_map = self.source.get_parent_map(revision_ids)
3201
3129
        for tree in self.source.revision_trees(revision_ids):
3202
3130
            current_revision_id = tree.get_revision_id()
3203
 
            delta = tree.inventory._make_delta(basis_tree.inventory)
 
3131
            parent_ids = parent_map.get(current_revision_id, ())
 
3132
            basis_id, delta = self._get_delta_for_revision(tree, parent_ids,
 
3133
                                                           basis_id, cache)
 
3134
            # Find text entries that need to be copied
3204
3135
            for old_path, new_path, file_id, entry in delta:
3205
3136
                if new_path is not None:
3206
3137
                    if not (new_path or self.target.supports_rich_root()):
3207
 
                        # We leave the inventory delta in, because that
3208
 
                        # will have the deserialised inventory root
3209
 
                        # pointer.
 
3138
                        # We don't copy the text for the root node unless the
 
3139
                        # target supports_rich_root.
3210
3140
                        continue
3211
3141
                    # TODO: Do we need:
3212
3142
                    #       "if entry.revision == current_revision_id" ?
3216
3146
            pending_deltas.append((basis_id, delta,
3217
3147
                current_revision_id, revision.parent_ids))
3218
3148
            pending_revisions.append(revision)
 
3149
            cache[current_revision_id] = tree
3219
3150
            basis_id = current_revision_id
3220
 
            basis_tree = tree
3221
3151
        # Copy file texts
3222
3152
        from_texts = self.source.texts
3223
3153
        to_texts = self.target.texts
3224
3154
        to_texts.insert_record_stream(from_texts.get_record_stream(
3225
 
            text_keys, self.target._fetch_order,
3226
 
            not self.target._fetch_uses_deltas))
 
3155
            text_keys, self.target._format._fetch_order,
 
3156
            not self.target._format._fetch_uses_deltas))
3227
3157
        # insert deltas
3228
3158
        for delta in pending_deltas:
3229
3159
            self.target.add_inventory_by_delta(*delta)
3237
3167
            except errors.NoSuchRevision:
3238
3168
                pass
3239
3169
            self.target.add_revision(revision.revision_id, revision)
3240
 
        return basis_id, basis_tree
 
3170
        return basis_id
3241
3171
 
3242
3172
    def _fetch_all_revisions(self, revision_ids, pb):
3243
3173
        """Fetch everything for the list of revisions.
3249
3179
        """
3250
3180
        basis_id, basis_tree = self._get_basis(revision_ids[0])
3251
3181
        batch_size = 100
 
3182
        cache = lru_cache.LRUCache(100)
 
3183
        cache[basis_id] = basis_tree
 
3184
        del basis_tree # We don't want to hang on to it here
3252
3185
        for offset in range(0, len(revision_ids), batch_size):
3253
3186
            self.target.start_write_group()
3254
3187
            try:
3255
3188
                pb.update('Transferring revisions', offset,
3256
3189
                          len(revision_ids))
3257
3190
                batch = revision_ids[offset:offset+batch_size]
3258
 
                basis_id, basis_tree = self._fetch_batch(batch,
3259
 
                    basis_id, basis_tree)
 
3191
                basis_id = self._fetch_batch(batch, basis_id, cache)
3260
3192
            except:
3261
3193
                self.target.abort_write_group()
3262
3194
                raise
3334
3266
            # Make _real_inter use the RemoteRepository for get_parent_map
3335
3267
            self._real_inter.target_get_graph = self.target.get_graph
3336
3268
            self._real_inter.target_get_parent_map = self.target.get_parent_map
3337
 
    
 
3269
 
3338
3270
    def copy_content(self, revision_id=None):
3339
3271
        self._ensure_real_inter()
3340
3272
        self._real_inter.copy_content(revision_id=revision_id)
3359
3291
    def is_compatible(source, target):
3360
3292
        if not isinstance(source, remote.RemoteRepository):
3361
3293
            return False
3362
 
        # Is source's model compatible with target's model?
3363
 
        source._ensure_real()
3364
 
        real_source = source._real_repository
3365
 
        if isinstance(real_source, remote.RemoteRepository):
3366
 
            raise NotImplementedError(
3367
 
                "We don't support remote repos backed by remote repos yet.")
3368
 
        return InterRepository._same_model(real_source, target)
 
3294
        return InterRepository._same_model(source, target)
3369
3295
 
3370
3296
    def _ensure_real_inter(self):
3371
3297
        if self._real_inter is None:
3372
3298
            self.source._ensure_real()
3373
3299
            real_source = self.source._real_repository
3374
3300
            self._real_inter = InterRepository.get(real_source, self.target)
3375
 
    
 
3301
 
 
3302
    @needs_write_lock
3376
3303
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3377
 
        self._ensure_real_inter()
3378
 
        return self._real_inter.fetch(revision_id=revision_id, pb=pb,
3379
 
            find_ghosts=find_ghosts)
 
3304
        """See InterRepository.fetch()."""
 
3305
        # Always fetch using the generic streaming fetch code, to allow
 
3306
        # streaming fetching from remote servers.
 
3307
        from bzrlib.fetch import RepoFetcher
 
3308
        fetcher = RepoFetcher(self.target, self.source, revision_id,
 
3309
                              pb, find_ghosts)
3380
3310
 
3381
3311
    def copy_content(self, revision_id=None):
3382
3312
        self._ensure_real_inter()
3403
3333
        from bzrlib.repofmt.pack_repo import RepositoryFormatPack
3404
3334
        if isinstance(source._format, RepositoryFormatPack):
3405
3335
            if isinstance(target, remote.RemoteRepository):
3406
 
                target._ensure_real()
3407
 
                if isinstance(target._real_repository._format,
 
3336
                target._format._ensure_real()
 
3337
                if isinstance(target._format._custom_format,
3408
3338
                              RepositoryFormatPack):
3409
3339
                    if InterRepository._same_model(source, target):
3410
3340
                        return True
3411
3341
        return False
3412
 
    
 
3342
 
3413
3343
    def _autopack(self):
3414
3344
        self.target.autopack()
3415
 
        
 
3345
 
 
3346
    @needs_write_lock
 
3347
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
 
3348
        """See InterRepository.fetch()."""
 
3349
        # Always fetch using the generic streaming fetch code, to allow
 
3350
        # streaming fetching into remote servers.
 
3351
        from bzrlib.fetch import RepoFetcher
 
3352
        fetcher = RepoFetcher(self.target, self.source, revision_id,
 
3353
                              pb, find_ghosts)
 
3354
 
3416
3355
    def _get_target_pack_collection(self):
3417
3356
        return self.target._real_repository._pack_collection
3418
3357
 
3425
3364
InterRepository.register_optimiser(InterSameDataRepository)
3426
3365
InterRepository.register_optimiser(InterWeaveRepo)
3427
3366
InterRepository.register_optimiser(InterKnitRepo)
3428
 
InterRepository.register_optimiser(InterModel1and2)
3429
 
InterRepository.register_optimiser(InterKnit1and2)
3430
3367
InterRepository.register_optimiser(InterPackRepo)
3431
3368
InterRepository.register_optimiser(InterOtherToRemote)
3432
3369
InterRepository.register_optimiser(InterRemoteToOther)
3435
3372
 
3436
3373
class CopyConverter(object):
3437
3374
    """A repository conversion tool which just performs a copy of the content.
3438
 
    
 
3375
 
3439
3376
    This is slow but quite reliable.
3440
3377
    """
3441
3378
 
3445
3382
        :param target_format: The format the resulting repository should be.
3446
3383
        """
3447
3384
        self.target_format = target_format
3448
 
        
 
3385
 
3449
3386
    def convert(self, repo, pb):
3450
3387
        """Perform the conversion of to_convert, giving feedback via pb.
3451
3388
 
3520
3457
    def __init__(self, repository):
3521
3458
        self.repository = repository
3522
3459
        self.text_index = self.repository._generate_text_key_index()
3523
 
    
 
3460
 
3524
3461
    def calculate_file_version_parents(self, text_key):
3525
3462
        """Calculate the correct parents for a file version according to
3526
3463
        the inventories.
3587
3524
        revision_graph[key] = tuple(parent for parent in parents if parent
3588
3525
            in revision_graph)
3589
3526
    return revision_graph
 
3527
 
 
3528
 
 
3529
class StreamSink(object):
 
3530
    """An object that can insert a stream into a repository.
 
3531
 
 
3532
    This interface handles the complexity of reserialising inventories and
 
3533
    revisions from different formats, and allows unidirectional insertion into
 
3534
    stacked repositories without looking for the missing basis parents
 
3535
    beforehand.
 
3536
    """
 
3537
 
 
3538
    def __init__(self, target_repo):
 
3539
        self.target_repo = target_repo
 
3540
 
 
3541
    def insert_stream(self, stream, src_format, resume_tokens):
 
3542
        """Insert a stream's content into the target repository.
 
3543
 
 
3544
        :param src_format: a bzr repository format.
 
3545
 
 
3546
        :return: a list of resume tokens and an  iterable of keys additional
 
3547
            items required before the insertion can be completed.
 
3548
        """
 
3549
        self.target_repo.lock_write()
 
3550
        try:
 
3551
            if resume_tokens:
 
3552
                self.target_repo.resume_write_group(resume_tokens)
 
3553
            else:
 
3554
                self.target_repo.start_write_group()
 
3555
            try:
 
3556
                # locked_insert_stream performs a commit|suspend.
 
3557
                return self._locked_insert_stream(stream, src_format)
 
3558
            except:
 
3559
                self.target_repo.abort_write_group(suppress_errors=True)
 
3560
                raise
 
3561
        finally:
 
3562
            self.target_repo.unlock()
 
3563
 
 
3564
    def _locked_insert_stream(self, stream, src_format):
 
3565
        to_serializer = self.target_repo._format._serializer
 
3566
        src_serializer = src_format._serializer
 
3567
        for substream_type, substream in stream:
 
3568
            if substream_type == 'texts':
 
3569
                self.target_repo.texts.insert_record_stream(substream)
 
3570
            elif substream_type == 'inventories':
 
3571
                if src_serializer == to_serializer:
 
3572
                    self.target_repo.inventories.insert_record_stream(
 
3573
                        substream)
 
3574
                else:
 
3575
                    self._extract_and_insert_inventories(
 
3576
                        substream, src_serializer)
 
3577
            elif substream_type == 'revisions':
 
3578
                # This may fallback to extract-and-insert more often than
 
3579
                # required if the serializers are different only in terms of
 
3580
                # the inventory.
 
3581
                if src_serializer == to_serializer:
 
3582
                    self.target_repo.revisions.insert_record_stream(
 
3583
                        substream)
 
3584
                else:
 
3585
                    self._extract_and_insert_revisions(substream,
 
3586
                        src_serializer)
 
3587
            elif substream_type == 'signatures':
 
3588
                self.target_repo.signatures.insert_record_stream(substream)
 
3589
            else:
 
3590
                raise AssertionError('kaboom! %s' % (substream_type,))
 
3591
        try:
 
3592
            missing_keys = set()
 
3593
            for prefix, versioned_file in (
 
3594
                ('texts', self.target_repo.texts),
 
3595
                ('inventories', self.target_repo.inventories),
 
3596
                ('revisions', self.target_repo.revisions),
 
3597
                ('signatures', self.target_repo.signatures),
 
3598
                ):
 
3599
                missing_keys.update((prefix,) + key for key in
 
3600
                    versioned_file.get_missing_compression_parent_keys())
 
3601
        except NotImplementedError:
 
3602
            # cannot even attempt suspending, and missing would have failed
 
3603
            # during stream insertion.
 
3604
            missing_keys = set()
 
3605
        else:
 
3606
            if missing_keys:
 
3607
                # suspend the write group and tell the caller what we is
 
3608
                # missing. We know we can suspend or else we would not have
 
3609
                # entered this code path. (All repositories that can handle
 
3610
                # missing keys can handle suspending a write group).
 
3611
                write_group_tokens = self.target_repo.suspend_write_group()
 
3612
                return write_group_tokens, missing_keys
 
3613
        self.target_repo.commit_write_group()
 
3614
        return [], set()
 
3615
 
 
3616
    def _extract_and_insert_inventories(self, substream, serializer):
 
3617
        """Generate a new inventory versionedfile in target, converting data.
 
3618
 
 
3619
        The inventory is retrieved from the source, (deserializing it), and
 
3620
        stored in the target (reserializing it in a different format).
 
3621
        """
 
3622
        for record in substream:
 
3623
            bytes = record.get_bytes_as('fulltext')
 
3624
            revision_id = record.key[0]
 
3625
            inv = serializer.read_inventory_from_string(bytes, revision_id)
 
3626
            parents = [key[0] for key in record.parents]
 
3627
            self.target_repo.add_inventory(revision_id, inv, parents)
 
3628
 
 
3629
    def _extract_and_insert_revisions(self, substream, serializer):
 
3630
        for record in substream:
 
3631
            bytes = record.get_bytes_as('fulltext')
 
3632
            revision_id = record.key[0]
 
3633
            rev = serializer.read_revision_from_string(bytes)
 
3634
            if rev.revision_id != revision_id:
 
3635
                raise AssertionError('wtf: %s != %s' % (rev, revision_id))
 
3636
            self.target_repo.add_revision(revision_id, rev)
 
3637
 
 
3638
    def finished(self):
 
3639
        if self.target_repo._format._fetch_reconcile:
 
3640
            self.target_repo.reconcile()
 
3641
 
 
3642
 
 
3643
class StreamSource(object):
 
3644
    """A source of a stream for fetching between repositories."""
 
3645
 
 
3646
    def __init__(self, from_repository, to_format):
 
3647
        """Create a StreamSource streaming from from_repository."""
 
3648
        self.from_repository = from_repository
 
3649
        self.to_format = to_format
 
3650
 
 
3651
    def delta_on_metadata(self):
 
3652
        """Return True if delta's are permitted on metadata streams.
 
3653
 
 
3654
        That is on revisions and signatures.
 
3655
        """
 
3656
        src_serializer = self.from_repository._format._serializer
 
3657
        target_serializer = self.to_format._serializer
 
3658
        return (self.to_format._fetch_uses_deltas and
 
3659
            src_serializer == target_serializer)
 
3660
 
 
3661
    def _fetch_revision_texts(self, revs):
 
3662
        # fetch signatures first and then the revision texts
 
3663
        # may need to be a InterRevisionStore call here.
 
3664
        from_sf = self.from_repository.signatures
 
3665
        # A missing signature is just skipped.
 
3666
        keys = [(rev_id,) for rev_id in revs]
 
3667
        signatures = versionedfile.filter_absent(from_sf.get_record_stream(
 
3668
            keys,
 
3669
            self.to_format._fetch_order,
 
3670
            not self.to_format._fetch_uses_deltas))
 
3671
        # If a revision has a delta, this is actually expanded inside the
 
3672
        # insert_record_stream code now, which is an alternate fix for
 
3673
        # bug #261339
 
3674
        from_rf = self.from_repository.revisions
 
3675
        revisions = from_rf.get_record_stream(
 
3676
            keys,
 
3677
            self.to_format._fetch_order,
 
3678
            not self.delta_on_metadata())
 
3679
        return [('signatures', signatures), ('revisions', revisions)]
 
3680
 
 
3681
    def _generate_root_texts(self, revs):
 
3682
        """This will be called by __fetch between fetching weave texts and
 
3683
        fetching the inventory weave.
 
3684
 
 
3685
        Subclasses should override this if they need to generate root texts
 
3686
        after fetching weave texts.
 
3687
        """
 
3688
        if self._rich_root_upgrade():
 
3689
            import bzrlib.fetch
 
3690
            return bzrlib.fetch.Inter1and2Helper(
 
3691
                self.from_repository).generate_root_texts(revs)
 
3692
        else:
 
3693
            return []
 
3694
 
 
3695
    def get_stream(self, search):
 
3696
        phase = 'file'
 
3697
        revs = search.get_keys()
 
3698
        graph = self.from_repository.get_graph()
 
3699
        revs = list(graph.iter_topo_order(revs))
 
3700
        data_to_fetch = self.from_repository.item_keys_introduced_by(revs)
 
3701
        text_keys = []
 
3702
        for knit_kind, file_id, revisions in data_to_fetch:
 
3703
            if knit_kind != phase:
 
3704
                phase = knit_kind
 
3705
                # Make a new progress bar for this phase
 
3706
            if knit_kind == "file":
 
3707
                # Accumulate file texts
 
3708
                text_keys.extend([(file_id, revision) for revision in
 
3709
                    revisions])
 
3710
            elif knit_kind == "inventory":
 
3711
                # Now copy the file texts.
 
3712
                from_texts = self.from_repository.texts
 
3713
                yield ('texts', from_texts.get_record_stream(
 
3714
                    text_keys, self.to_format._fetch_order,
 
3715
                    not self.to_format._fetch_uses_deltas))
 
3716
                # Cause an error if a text occurs after we have done the
 
3717
                # copy.
 
3718
                text_keys = None
 
3719
                # Before we process the inventory we generate the root
 
3720
                # texts (if necessary) so that the inventories references
 
3721
                # will be valid.
 
3722
                for _ in self._generate_root_texts(revs):
 
3723
                    yield _
 
3724
                # NB: This currently reopens the inventory weave in source;
 
3725
                # using a single stream interface instead would avoid this.
 
3726
                from_weave = self.from_repository.inventories
 
3727
                # we fetch only the referenced inventories because we do not
 
3728
                # know for unselected inventories whether all their required
 
3729
                # texts are present in the other repository - it could be
 
3730
                # corrupt.
 
3731
                yield ('inventories', from_weave.get_record_stream(
 
3732
                    [(rev_id,) for rev_id in revs],
 
3733
                    self.inventory_fetch_order(),
 
3734
                    not self.delta_on_metadata()))
 
3735
            elif knit_kind == "signatures":
 
3736
                # Nothing to do here; this will be taken care of when
 
3737
                # _fetch_revision_texts happens.
 
3738
                pass
 
3739
            elif knit_kind == "revisions":
 
3740
                for record in self._fetch_revision_texts(revs):
 
3741
                    yield record
 
3742
            else:
 
3743
                raise AssertionError("Unknown knit kind %r" % knit_kind)
 
3744
 
 
3745
    def get_stream_for_missing_keys(self, missing_keys):
 
3746
        # missing keys can only occur when we are byte copying and not
 
3747
        # translating (because translation means we don't send
 
3748
        # unreconstructable deltas ever).
 
3749
        keys = {}
 
3750
        keys['texts'] = set()
 
3751
        keys['revisions'] = set()
 
3752
        keys['inventories'] = set()
 
3753
        keys['signatures'] = set()
 
3754
        for key in missing_keys:
 
3755
            keys[key[0]].add(key[1:])
 
3756
        if len(keys['revisions']):
 
3757
            # If we allowed copying revisions at this point, we could end up
 
3758
            # copying a revision without copying its required texts: a
 
3759
            # violation of the requirements for repository integrity.
 
3760
            raise AssertionError(
 
3761
                'cannot copy revisions to fill in missing deltas %s' % (
 
3762
                    keys['revisions'],))
 
3763
        for substream_kind, keys in keys.iteritems():
 
3764
            vf = getattr(self.from_repository, substream_kind)
 
3765
            # Ask for full texts always so that we don't need more round trips
 
3766
            # after this stream.
 
3767
            stream = vf.get_record_stream(keys,
 
3768
                self.to_format._fetch_order, True)
 
3769
            yield substream_kind, stream
 
3770
 
 
3771
    def inventory_fetch_order(self):
 
3772
        if self._rich_root_upgrade():
 
3773
            return 'topological'
 
3774
        else:
 
3775
            return self.to_format._fetch_order
 
3776
 
 
3777
    def _rich_root_upgrade(self):
 
3778
        return (not self.from_repository._format.rich_root_data and
 
3779
            self.to_format.rich_root_data)
 
3780