~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

merge bzr.dev r4054

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:
643
643
        """Check that this repository can fallback to repository safely.
644
644
 
645
645
        Raise an error if not.
646
 
        
 
646
 
647
647
        :param repository: A repository to fallback to.
648
648
        """
649
649
        return InterRepository._assert_same_model(self, repository)
650
650
 
651
651
    def add_inventory(self, revision_id, inv, parents):
652
652
        """Add the inventory inv to the repository as revision_id.
653
 
        
 
653
 
654
654
        :param parents: The revision ids of the parents that revision_id
655
655
                        is known to have and are in the repository already.
656
656
 
758
758
        self.revisions.add_lines(key, parents, osutils.split_lines(text))
759
759
 
760
760
    def all_revision_ids(self):
761
 
        """Returns a list of all the revision ids in the repository. 
 
761
        """Returns a list of all the revision ids in the repository.
762
762
 
763
763
        This is conceptually deprecated because code should generally work on
764
764
        the graph reachable from a particular revision, and ignore any other
770
770
        return self._all_revision_ids()
771
771
 
772
772
    def _all_revision_ids(self):
773
 
        """Returns a list of all the revision ids in the repository. 
 
773
        """Returns a list of all the revision ids in the repository.
774
774
 
775
 
        These are in as much topological order as the underlying store can 
 
775
        These are in as much topological order as the underlying store can
776
776
        present.
777
777
        """
778
778
        raise NotImplementedError(self._all_revision_ids)
823
823
        self._reconcile_does_inventory_gc = True
824
824
        self._reconcile_fixes_text_parents = False
825
825
        self._reconcile_backsup_inventory = True
826
 
        # not right yet - should be more semantically clear ? 
827
 
        # 
 
826
        # not right yet - should be more semantically clear ?
 
827
        #
828
828
        # TODO: make sure to construct the right store classes, etc, depending
829
829
        # on whether escaping is required.
830
830
        self._warn_if_deprecated()
881
881
        This causes caching within the repository obejct to start accumlating
882
882
        data during reads, and allows a 'write_group' to be obtained. Write
883
883
        groups must be used for actual data insertion.
884
 
        
 
884
 
885
885
        :param token: if this is already locked, then lock_write will fail
886
886
            unless the token matches the existing lock.
887
887
        :returns: a token if this instance supports tokens, otherwise None.
916
916
    def leave_lock_in_place(self):
917
917
        """Tell this repository not to release the physical lock when this
918
918
        object is unlocked.
919
 
        
 
919
 
920
920
        If lock_write doesn't return a token, then this method is not supported.
921
921
        """
922
922
        self.control_files.leave_in_place()
1028
1028
    @needs_read_lock
1029
1029
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1030
1030
        """Return the revision ids that other has that this does not.
1031
 
        
 
1031
 
1032
1032
        These are returned in topological order.
1033
1033
 
1034
1034
        revision_id: only return revision ids included by revision_id.
1040
1040
    @needs_read_lock
1041
1041
    def missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1042
1042
        """Return the revision ids that other has that this does not.
1043
 
        
 
1043
 
1044
1044
        These are returned in topological order.
1045
1045
 
1046
1046
        revision_id: only return revision ids included by revision_id.
1066
1066
 
1067
1067
    def copy_content_into(self, destination, revision_id=None):
1068
1068
        """Make a complete copy of the content in self into destination.
1069
 
        
1070
 
        This is a destructive operation! Do not use it on existing 
 
1069
 
 
1070
        This is a destructive operation! Do not use it on existing
1071
1071
        repositories.
1072
1072
        """
1073
1073
        return InterRepository.get(self, destination).copy_content(revision_id)
1087
1087
 
1088
1088
    def _commit_write_group(self):
1089
1089
        """Template method for per-repository write group cleanup.
1090
 
        
1091
 
        This is called before the write group is considered to be 
 
1090
 
 
1091
        This is called before the write group is considered to be
1092
1092
        finished and should ensure that all data handed to the repository
1093
 
        for writing during the write group is safely committed (to the 
 
1093
        for writing during the write group is safely committed (to the
1094
1094
        extent possible considering file system caching etc).
1095
1095
        """
1096
1096
 
 
1097
    def suspend_write_group(self):
 
1098
        raise errors.UnsuspendableWriteGroup(self)
 
1099
 
 
1100
    def resume_write_group(self, tokens):
 
1101
        if not self.is_write_locked():
 
1102
            raise errors.NotWriteLocked(self)
 
1103
        if self._write_group:
 
1104
            raise errors.BzrError('already in a write group')
 
1105
        self._resume_write_group(tokens)
 
1106
        # so we can detect unlock/relock - the write group is now entered.
 
1107
        self._write_group = self.get_transaction()
 
1108
 
 
1109
    def _resume_write_group(self, tokens):
 
1110
        raise errors.UnsuspendableWriteGroup(self)
 
1111
 
1097
1112
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False):
1098
1113
        """Fetch the content required to construct revision_id from source.
1099
1114
 
1124
1139
                           timezone=None, committer=None, revprops=None,
1125
1140
                           revision_id=None):
1126
1141
        """Obtain a CommitBuilder for this repository.
1127
 
        
 
1142
 
1128
1143
        :param branch: Branch to commit to.
1129
1144
        :param parents: Revision ids of the parents of the new revision.
1130
1145
        :param config: Configuration to use.
1192
1207
 
1193
1208
    def _start_write_group(self):
1194
1209
        """Template method for per-repository write group startup.
1195
 
        
1196
 
        This is called before the write group is considered to be 
 
1210
 
 
1211
        This is called before the write group is considered to be
1197
1212
        entered.
1198
1213
        """
1199
1214
 
1220
1235
                dest_repo = a_bzrdir.open_repository()
1221
1236
        return dest_repo
1222
1237
 
 
1238
    def _get_sink(self):
 
1239
        """Return a sink for streaming into this repository."""
 
1240
        return StreamSink(self)
 
1241
 
1223
1242
    @needs_read_lock
1224
1243
    def has_revision(self, revision_id):
1225
1244
        """True if this repository has a copy of the revision."""
1248
1267
    @needs_read_lock
1249
1268
    def get_revision_reconcile(self, revision_id):
1250
1269
        """'reconcile' helper routine that allows access to a revision always.
1251
 
        
 
1270
 
1252
1271
        This variant of get_revision does not cross check the weave graph
1253
1272
        against the revision one as get_revision does: but it should only
1254
1273
        be used by reconcile, or reconcile-alike commands that are correcting
1292
1311
 
1293
1312
    def get_deltas_for_revisions(self, revisions):
1294
1313
        """Produce a generator of revision deltas.
1295
 
        
 
1314
 
1296
1315
        Note that the input is a sequence of REVISIONS, not revision_ids.
1297
1316
        Trees will be held in memory until the generator exits.
1298
1317
        Each delta is relative to the revision's lefthand predecessor.
1301
1320
        for revision in revisions:
1302
1321
            required_trees.add(revision.revision_id)
1303
1322
            required_trees.update(revision.parent_ids[:1])
1304
 
        trees = dict((t.get_revision_id(), t) for 
 
1323
        trees = dict((t.get_revision_id(), t) for
1305
1324
                     t in self.revision_trees(required_trees))
1306
1325
        for revision in revisions:
1307
1326
            if not revision.parent_ids:
1372
1391
 
1373
1392
        # this code needs to read every new line in every inventory for the
1374
1393
        # inventories [revision_ids]. Seeing a line twice is ok. Seeing a line
1375
 
        # not present in one of those inventories is unnecessary but not 
 
1394
        # not present in one of those inventories is unnecessary but not
1376
1395
        # 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  
 
1396
        # inventory lines : we only select file ids altered in one of those
1378
1397
        # revisions. We don't need to see all lines in the inventory because
1379
1398
        # only those added in an inventory in rev X can contain a revision=X
1380
1399
        # line.
1695
1714
            yield ''.join(chunks), key[-1]
1696
1715
 
1697
1716
    def deserialise_inventory(self, revision_id, xml):
1698
 
        """Transform the xml into an inventory object. 
 
1717
        """Transform the xml into an inventory object.
1699
1718
 
1700
1719
        :param revision_id: The expected revision id of the inventory.
1701
1720
        :param xml: A serialised inventory.
1803
1822
        # TODO: refactor this to use an existing revision object
1804
1823
        # so we don't need to read it in twice.
1805
1824
        if revision_id == _mod_revision.NULL_REVISION:
1806
 
            return RevisionTree(self, Inventory(root_id=None), 
 
1825
            return RevisionTree(self, Inventory(root_id=None),
1807
1826
                                _mod_revision.NULL_REVISION)
1808
1827
        else:
1809
1828
            inv = self.get_revision_inventory(revision_id)
1821
1840
    def get_ancestry(self, revision_id, topo_sorted=True):
1822
1841
        """Return a list of revision-ids integrated by a revision.
1823
1842
 
1824
 
        The first element of the list is always None, indicating the origin 
1825
 
        revision.  This might change when we have history horizons, or 
 
1843
        The first element of the list is always None, indicating the origin
 
1844
        revision.  This might change when we have history horizons, or
1826
1845
        perhaps we should have a new API.
1827
 
        
 
1846
 
1828
1847
        This is topologically sorted.
1829
1848
        """
1830
1849
        if _mod_revision.is_null(revision_id):
1854
1873
        types it should be a no-op that just returns.
1855
1874
 
1856
1875
        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 
 
1876
        @needs_write_lock as this is a long running call its reasonable to
1858
1877
        implicitly lock for the user.
1859
1878
        """
1860
1879
 
1862
1881
    @deprecated_method(one_six)
1863
1882
    def print_file(self, file, revision_id):
1864
1883
        """Print `file` to stdout.
1865
 
        
 
1884
 
1866
1885
        FIXME RBC 20060125 as John Meinel points out this is a bad api
1867
1886
        - it writes to stdout, it assumes that that is valid etc. Fix
1868
1887
        by creating a new more flexible convenience function.
1948
1967
                          working trees.
1949
1968
        """
1950
1969
        raise NotImplementedError(self.set_make_working_trees)
1951
 
    
 
1970
 
1952
1971
    def make_working_trees(self):
1953
1972
        """Returns the policy for making working trees on new branches."""
1954
1973
        raise NotImplementedError(self.make_working_trees)
2019
2038
                    revision_id.decode('ascii')
2020
2039
                except UnicodeDecodeError:
2021
2040
                    raise errors.NonAsciiRevisionId(method, self)
2022
 
    
 
2041
 
2023
2042
    def revision_graph_can_have_wrong_parents(self):
2024
2043
        """Is it possible for this repository to have a revision graph with
2025
2044
        incorrect parents?
2143
2162
 
2144
2163
class MetaDirRepository(Repository):
2145
2164
    """Repositories in the new meta-dir layout.
2146
 
    
 
2165
 
2147
2166
    :ivar _transport: Transport for access to repository control files,
2148
2167
        typically pointing to .bzr/repository.
2149
2168
    """
2174
2193
        else:
2175
2194
            self._transport.put_bytes('no-working-trees', '',
2176
2195
                mode=self.bzrdir._get_file_mode())
2177
 
    
 
2196
 
2178
2197
    def make_working_trees(self):
2179
2198
        """Returns the policy for making working trees on new branches."""
2180
2199
        return not self._transport.has('no-working-trees')
2188
2207
            control_files)
2189
2208
 
2190
2209
 
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.
 
2210
network_format_registry = registry.FormatRegistry()
 
2211
"""Registry of formats indexed by their network name.
 
2212
 
 
2213
The network name for a repository format is an identifier that can be used when
 
2214
referring to formats with smart server operations. See
 
2215
RepositoryFormat.network_name() for more detail.
 
2216
"""
 
2217
 
 
2218
 
 
2219
format_registry = registry.FormatRegistry(network_format_registry)
 
2220
"""Registry of formats, indexed by their BzrDirMetaFormat format string.
2203
2221
 
2204
2222
This can contain either format instances themselves, or classes/factories that
2205
2223
can be called to obtain one.
2212
2230
class RepositoryFormat(object):
2213
2231
    """A repository format.
2214
2232
 
2215
 
    Formats provide three things:
 
2233
    Formats provide four things:
2216
2234
     * An initialization routine to construct repository data on disk.
2217
 
     * a format string which is used when the BzrDir supports versioned
2218
 
       children.
 
2235
     * a optional format string which is used when the BzrDir supports
 
2236
       versioned children.
2219
2237
     * an open routine which returns a Repository instance.
 
2238
     * A network name for referring to the format in smart server RPC
 
2239
       methods.
2220
2240
 
2221
2241
    There is one and only one Format subclass for each on-disk format. But
2222
2242
    there can be one Repository subclass that is used for several different
2223
2243
    formats. The _format attribute on a Repository instance can be used to
2224
2244
    determine the disk format.
2225
2245
 
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.
 
2246
    Formats are placed in a registry by their format string for reference
 
2247
    during opening. These should be subclasses of RepositoryFormat for
 
2248
    consistency.
2229
2249
 
2230
2250
    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.
 
2251
    methods on the format class. Do not deprecate the object, as the
 
2252
    object may be created even when a repository instnace hasn't been
 
2253
    created.
2233
2254
 
2234
2255
    Common instance attributes:
2235
2256
    _matchingbzrdir - the bzrdir format that the repository format was
2258
2279
    @classmethod
2259
2280
    def find_format(klass, a_bzrdir):
2260
2281
        """Return the format for the repository object in a_bzrdir.
2261
 
        
 
2282
 
2262
2283
        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 
 
2284
        the repository.  Other methods may be used by different types of
2264
2285
        control directory.
2265
2286
        """
2266
2287
        try:
2280
2301
    @classmethod
2281
2302
    def unregister_format(klass, format):
2282
2303
        format_registry.remove(format.get_format_string())
2283
 
    
 
2304
 
2284
2305
    @classmethod
2285
2306
    def get_default_format(klass):
2286
2307
        """Return the current default format."""
2289
2310
 
2290
2311
    def get_format_string(self):
2291
2312
        """Return the ASCII format string that identifies this format.
2292
 
        
2293
 
        Note that in pre format ?? repositories the format string is 
 
2313
 
 
2314
        Note that in pre format ?? repositories the format string is
2294
2315
        not permitted nor written to disk.
2295
2316
        """
2296
2317
        raise NotImplementedError(self.get_format_string)
2327
2348
        :param a_bzrdir: The bzrdir to put the new repository in it.
2328
2349
        :param shared: The repository should be initialized as a sharable one.
2329
2350
        :returns: The new repository object.
2330
 
        
 
2351
 
2331
2352
        This may raise UninitializableFormat if shared repository are not
2332
2353
        compatible the a_bzrdir.
2333
2354
        """
2337
2358
        """Is this format supported?
2338
2359
 
2339
2360
        Supported formats must be initializable and openable.
2340
 
        Unsupported formats may not support initialization or committing or 
 
2361
        Unsupported formats may not support initialization or committing or
2341
2362
        some other features depending on the reason for not being supported.
2342
2363
        """
2343
2364
        return True
2344
2365
 
 
2366
    def network_name(self):
 
2367
        """A simple byte string uniquely identifying this format for RPC calls.
 
2368
 
 
2369
        MetaDir repository formats use their disk format string to identify the
 
2370
        repository over the wire. All in one formats such as bzr < 0.8, and
 
2371
        foreign formats like svn/git and hg should use some marker which is
 
2372
        unique and immutable.
 
2373
        """
 
2374
        raise NotImplementedError(self.network_name)
 
2375
 
2345
2376
    def check_conversion_target(self, target_format):
2346
2377
        raise NotImplementedError(self.check_conversion_target)
2347
2378
 
2348
2379
    def open(self, a_bzrdir, _found=False):
2349
2380
        """Return an instance of this format for the bzrdir a_bzrdir.
2350
 
        
 
2381
 
2351
2382
        _found is a private parameter, do not use it.
2352
2383
        """
2353
2384
        raise NotImplementedError(self.open)
2397
2428
        finally:
2398
2429
            control_files.unlock()
2399
2430
 
2400
 
 
2401
 
# formats which have no format string are not discoverable
2402
 
# and not independently creatable, so are not registered.  They're 
 
2431
    def network_name(self):
 
2432
        """Metadir formats have matching disk and network format strings."""
 
2433
        return self.get_format_string()
 
2434
 
 
2435
 
 
2436
# Pre-0.8 formats that don't have a disk format string (because they are
 
2437
# versioned by the matching control directory). We use the control directories
 
2438
# disk format string as a key for the network_name because they meet the
 
2439
# constraints (simple string, unique, immmutable).
 
2440
network_format_registry.register_lazy(
 
2441
    "Bazaar-NG branch, format 5\n",
 
2442
    'bzrlib.repofmt.weaverepo',
 
2443
    'RepositoryFormat5',
 
2444
)
 
2445
network_format_registry.register_lazy(
 
2446
    "Bazaar-NG branch, format 6\n",
 
2447
    'bzrlib.repofmt.weaverepo',
 
2448
    'RepositoryFormat6',
 
2449
)
 
2450
 
 
2451
# formats which have no format string are not discoverable or independently
 
2452
# creatable on disk, so are not registered in format_registry.  They're
2403
2453
# all in bzrlib.repofmt.weaverepo now.  When an instance of one of these is
2404
2454
# needed, it's constructed directly by the BzrDir.  Non-native formats where
2405
2455
# the repository is not separately opened are similar.
2472
2522
    'RepositoryFormatKnitPack6RichRoot',
2473
2523
    )
2474
2524
 
2475
 
# Development formats. 
 
2525
# Development formats.
2476
2526
# 1.7->1.8 go below here
2477
2527
format_registry.register_lazy(
2478
2528
    "Bazaar development format 2 (needs bzr.dev from before 1.8)\n",
2491
2541
    """This class represents operations taking place between two repositories.
2492
2542
 
2493
2543
    Its instances have methods like copy_content and fetch, and contain
2494
 
    references to the source and target repositories these operations can be 
 
2544
    references to the source and target repositories these operations can be
2495
2545
    carried out on.
2496
2546
 
2497
2547
    Often we will provide convenience methods on 'repository' which carry out
2597
2647
    @needs_read_lock
2598
2648
    def missing_revision_ids(self, revision_id=None, find_ghosts=True):
2599
2649
        """Return the revision ids that source has that target does not.
2600
 
        
 
2650
 
2601
2651
        These are returned in topological order.
2602
2652
 
2603
2653
        :param revision_id: only return revision ids included by this
2611
2661
    @needs_read_lock
2612
2662
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
2613
2663
        """Return the revision ids that source has that target does not.
2614
 
        
 
2664
 
2615
2665
        :param revision_id: only return revision ids included by this
2616
2666
                            revision_id.
2617
2667
        :param find_ghosts: If True find missing revisions in deep history
2636
2686
    @staticmethod
2637
2687
    def _same_model(source, target):
2638
2688
        """True if source and target have the same data representation.
2639
 
        
 
2689
 
2640
2690
        Note: this is always called on the base class; overriding it in a
2641
2691
        subclass will have no effect.
2642
2692
        """
2660
2710
 
2661
2711
class InterSameDataRepository(InterRepository):
2662
2712
    """Code for converting between repositories that represent the same data.
2663
 
    
 
2713
 
2664
2714
    Data format and model must match for this to work.
2665
2715
    """
2666
2716
 
2667
2717
    @classmethod
2668
2718
    def _get_repo_format_to_test(self):
2669
2719
        """Repository format for testing with.
2670
 
        
 
2720
 
2671
2721
        InterSameData can pull from subtree to subtree and from non-subtree to
2672
2722
        non-subtree, so we test this with the richest repository format.
2673
2723
        """
2684
2734
 
2685
2735
        This copies both the repository's revision data, and configuration information
2686
2736
        such as the make_working_trees setting.
2687
 
        
2688
 
        This is a destructive operation! Do not use it on existing 
 
2737
 
 
2738
        This is a destructive operation! Do not use it on existing
2689
2739
        repositories.
2690
2740
 
2691
2741
        :param revision_id: Only copy the content needed to construct
2696
2746
        except NotImplementedError:
2697
2747
            pass
2698
2748
        # but don't bother fetching if we have the needed data now.
2699
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
 
2749
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and
2700
2750
            self.target.has_revision(revision_id)):
2701
2751
            return
2702
2752
        self.target.fetch(self.source, revision_id=revision_id)
2717
2767
 
2718
2768
class InterWeaveRepo(InterSameDataRepository):
2719
2769
    """Optimised code paths between Weave based repositories.
2720
 
    
 
2770
 
2721
2771
    This should be in bzrlib/repofmt/weaverepo.py but we have not yet
2722
2772
    implemented lazy inter-object optimisation.
2723
2773
    """
2730
2780
    @staticmethod
2731
2781
    def is_compatible(source, target):
2732
2782
        """Be compatible with known Weave formats.
2733
 
        
 
2783
 
2734
2784
        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 
 
2785
        could lead to confusing results, and there is no need to be
2736
2786
        overly general.
2737
2787
        """
2738
2788
        from bzrlib.repofmt.weaverepo import (
2749
2799
                                                RepositoryFormat7)))
2750
2800
        except AttributeError:
2751
2801
            return False
2752
 
    
 
2802
 
2753
2803
    @needs_write_lock
2754
2804
    def copy_content(self, revision_id=None):
2755
2805
        """See InterRepository.copy_content()."""
2799
2849
        """See InterRepository.missing_revision_ids()."""
2800
2850
        # we want all revisions to satisfy revision_id in source.
2801
2851
        # but we don't want to stat every file here and there.
2802
 
        # we want then, all revisions other needs to satisfy revision_id 
 
2852
        # we want then, all revisions other needs to satisfy revision_id
2803
2853
        # checked, but not those that we have locally.
2804
 
        # so the first thing is to get a subset of the revisions to 
 
2854
        # so the first thing is to get a subset of the revisions to
2805
2855
        # satisfy revision_id in source, and then eliminate those that
2806
 
        # we do already have. 
 
2856
        # we do already have.
2807
2857
        # this is slow on high latency connection to self, but as as this
2808
 
        # disk format scales terribly for push anyway due to rewriting 
 
2858
        # disk format scales terribly for push anyway due to rewriting
2809
2859
        # inventory.weave, this is considered acceptable.
2810
2860
        # - RBC 20060209
2811
2861
        if revision_id is not None:
2831
2881
            # and the tip revision was validated by get_ancestry.
2832
2882
            result_set = required_revisions
2833
2883
        else:
2834
 
            # if we just grabbed the possibly available ids, then 
 
2884
            # if we just grabbed the possibly available ids, then
2835
2885
            # we only have an estimate of whats available and need to validate
2836
2886
            # that against the revision records.
2837
2887
            result_set = set(
2850
2900
    @staticmethod
2851
2901
    def is_compatible(source, target):
2852
2902
        """Be compatible with known Knit formats.
2853
 
        
 
2903
 
2854
2904
        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 
 
2905
        could lead to confusing results, and there is no need to be
2856
2906
        overly general.
2857
2907
        """
2858
2908
        from bzrlib.repofmt.knitrepo import RepositoryFormatKnit
2901
2951
            # and the tip revision was validated by get_ancestry.
2902
2952
            result_set = required_revisions
2903
2953
        else:
2904
 
            # if we just grabbed the possibly available ids, then 
 
2954
            # if we just grabbed the possibly available ids, then
2905
2955
            # we only have an estimate of whats available and need to validate
2906
2956
            # that against the revision records.
2907
2957
            result_set = set(
2920
2970
    @staticmethod
2921
2971
    def is_compatible(source, target):
2922
2972
        """Be compatible with known Pack formats.
2923
 
        
 
2973
 
2924
2974
        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 
 
2975
        could lead to confusing results, and there is no need to be
2926
2976
        overly general.
2927
2977
        """
2928
2978
        from bzrlib.repofmt.pack_repo import RepositoryFormatPack
3003
3053
 
3004
3054
    def _autopack(self):
3005
3055
        self.target._pack_collection.autopack()
3006
 
        
 
3056
 
3007
3057
    def _get_target_pack_collection(self):
3008
3058
        return self.target._pack_collection
3009
3059
 
3010
3060
    @needs_read_lock
3011
3061
    def search_missing_revision_ids(self, revision_id=None, find_ghosts=True):
3012
3062
        """See InterRepository.missing_revision_ids().
3013
 
        
 
3063
 
3014
3064
        :param find_ghosts: Find ghosts throughout the ancestry of
3015
3065
            revision_id.
3016
3066
        """
3073
3123
    @needs_write_lock
3074
3124
    def copy_content(self, revision_id=None):
3075
3125
        """Make a complete copy of the content in self into destination.
3076
 
        
3077
 
        This is a destructive operation! Do not use it on existing 
 
3126
 
 
3127
        This is a destructive operation! Do not use it on existing
3078
3128
        repositories.
3079
3129
 
3080
3130
        :param revision_id: Only copy the content needed to construct
3085
3135
        except NotImplementedError:
3086
3136
            pass
3087
3137
        # but don't bother fetching if we have the needed data now.
3088
 
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and 
 
3138
        if (revision_id not in (None, _mod_revision.NULL_REVISION) and
3089
3139
            self.target.has_revision(revision_id)):
3090
3140
            return
3091
3141
        self.target.fetch(self.source, revision_id=revision_id)
3155
3205
        """See InterRepository.fetch()."""
3156
3206
        from bzrlib.fetch import Knit1to2Fetcher
3157
3207
        mutter("Using fetch logic to copy between %s(%s) and %s(%s)",
3158
 
               self.source, self.source._format, self.target, 
 
3208
               self.source, self.source._format, self.target,
3159
3209
               self.target._format)
3160
3210
        f = Knit1to2Fetcher(to_repository=self.target,
3161
3211
                            from_repository=self.source,
3182
3232
            return False
3183
3233
        return True
3184
3234
 
3185
 
    def _fetch_batch(self, revision_ids, basis_id, basis_tree):
 
3235
    def _get_delta_for_revision(self, tree, parent_ids, basis_id, cache):
 
3236
        """Get the best delta and base for this revision.
 
3237
 
 
3238
        :return: (basis_id, delta)
 
3239
        """
 
3240
        possible_trees = [(parent_id, cache[parent_id])
 
3241
                          for parent_id in parent_ids
 
3242
                           if parent_id in cache]
 
3243
        if len(possible_trees) == 0:
 
3244
            # There either aren't any parents, or the parents aren't in the
 
3245
            # cache, so just use the last converted tree
 
3246
            possible_trees.append((basis_id, cache[basis_id]))
 
3247
        deltas = []
 
3248
        for basis_id, basis_tree in possible_trees:
 
3249
            delta = tree.inventory._make_delta(basis_tree.inventory)
 
3250
            deltas.append((len(delta), basis_id, delta))
 
3251
        deltas.sort()
 
3252
        return deltas[0][1:]
 
3253
 
 
3254
    def _fetch_batch(self, revision_ids, basis_id, cache):
3186
3255
        """Fetch across a few revisions.
3187
3256
 
3188
3257
        :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.
 
3258
        :param basis_id: The revision_id of a tree that must be in cache, used
 
3259
            as a basis for delta when no other base is available
 
3260
        :param cache: A cache of RevisionTrees that we can use.
 
3261
        :return: The revision_id of the last converted tree. The RevisionTree
 
3262
            for it will be in cache
3194
3263
        """
3195
3264
        # Walk though all revisions; get inventory deltas, copy referenced
3196
3265
        # texts that delta references, insert the delta, revision and
3198
3267
        text_keys = set()
3199
3268
        pending_deltas = []
3200
3269
        pending_revisions = []
 
3270
        parent_map = self.source.get_parent_map(revision_ids)
3201
3271
        for tree in self.source.revision_trees(revision_ids):
3202
3272
            current_revision_id = tree.get_revision_id()
3203
 
            delta = tree.inventory._make_delta(basis_tree.inventory)
 
3273
            parent_ids = parent_map.get(current_revision_id, ())
 
3274
            basis_id, delta = self._get_delta_for_revision(tree, parent_ids,
 
3275
                                                           basis_id, cache)
 
3276
            # Find text entries that need to be copied
3204
3277
            for old_path, new_path, file_id, entry in delta:
3205
3278
                if new_path is not None:
3206
3279
                    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.
 
3280
                        # We don't copy the text for the root node unless the
 
3281
                        # target supports_rich_root.
3210
3282
                        continue
3211
3283
                    # TODO: Do we need:
3212
3284
                    #       "if entry.revision == current_revision_id" ?
3216
3288
            pending_deltas.append((basis_id, delta,
3217
3289
                current_revision_id, revision.parent_ids))
3218
3290
            pending_revisions.append(revision)
 
3291
            cache[current_revision_id] = tree
3219
3292
            basis_id = current_revision_id
3220
 
            basis_tree = tree
3221
3293
        # Copy file texts
3222
3294
        from_texts = self.source.texts
3223
3295
        to_texts = self.target.texts
3237
3309
            except errors.NoSuchRevision:
3238
3310
                pass
3239
3311
            self.target.add_revision(revision.revision_id, revision)
3240
 
        return basis_id, basis_tree
 
3312
        return basis_id
3241
3313
 
3242
3314
    def _fetch_all_revisions(self, revision_ids, pb):
3243
3315
        """Fetch everything for the list of revisions.
3249
3321
        """
3250
3322
        basis_id, basis_tree = self._get_basis(revision_ids[0])
3251
3323
        batch_size = 100
 
3324
        cache = lru_cache.LRUCache(100)
 
3325
        cache[basis_id] = basis_tree
 
3326
        del basis_tree # We don't want to hang on to it here
3252
3327
        for offset in range(0, len(revision_ids), batch_size):
3253
3328
            self.target.start_write_group()
3254
3329
            try:
3255
3330
                pb.update('Transferring revisions', offset,
3256
3331
                          len(revision_ids))
3257
3332
                batch = revision_ids[offset:offset+batch_size]
3258
 
                basis_id, basis_tree = self._fetch_batch(batch,
3259
 
                    basis_id, basis_tree)
 
3333
                basis_id = self._fetch_batch(batch, basis_id, cache)
3260
3334
            except:
3261
3335
                self.target.abort_write_group()
3262
3336
                raise
3334
3408
            # Make _real_inter use the RemoteRepository for get_parent_map
3335
3409
            self._real_inter.target_get_graph = self.target.get_graph
3336
3410
            self._real_inter.target_get_parent_map = self.target.get_parent_map
3337
 
    
 
3411
 
3338
3412
    def copy_content(self, revision_id=None):
3339
3413
        self._ensure_real_inter()
3340
3414
        self._real_inter.copy_content(revision_id=revision_id)
3372
3446
            self.source._ensure_real()
3373
3447
            real_source = self.source._real_repository
3374
3448
            self._real_inter = InterRepository.get(real_source, self.target)
3375
 
    
 
3449
 
3376
3450
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
3377
3451
        self._ensure_real_inter()
3378
3452
        return self._real_inter.fetch(revision_id=revision_id, pb=pb,
3409
3483
                    if InterRepository._same_model(source, target):
3410
3484
                        return True
3411
3485
        return False
3412
 
    
 
3486
 
3413
3487
    def _autopack(self):
3414
3488
        self.target.autopack()
3415
 
        
 
3489
 
 
3490
    @needs_write_lock
 
3491
    def fetch(self, revision_id=None, pb=None, find_ghosts=False):
 
3492
        """See InterRepository.fetch()."""
 
3493
        # Always fetch using the generic streaming fetch code, to allow
 
3494
        # streaming fetching into remote servers.
 
3495
        from bzrlib.fetch import RepoFetcher
 
3496
        fetcher = RepoFetcher(self.target, self.source, revision_id,
 
3497
                              pb, find_ghosts)
 
3498
        return fetcher.count_copied, fetcher.failed_revisions
 
3499
 
3416
3500
    def _get_target_pack_collection(self):
3417
3501
        return self.target._real_repository._pack_collection
3418
3502
 
3435
3519
 
3436
3520
class CopyConverter(object):
3437
3521
    """A repository conversion tool which just performs a copy of the content.
3438
 
    
 
3522
 
3439
3523
    This is slow but quite reliable.
3440
3524
    """
3441
3525
 
3445
3529
        :param target_format: The format the resulting repository should be.
3446
3530
        """
3447
3531
        self.target_format = target_format
3448
 
        
 
3532
 
3449
3533
    def convert(self, repo, pb):
3450
3534
        """Perform the conversion of to_convert, giving feedback via pb.
3451
3535
 
3520
3604
    def __init__(self, repository):
3521
3605
        self.repository = repository
3522
3606
        self.text_index = self.repository._generate_text_key_index()
3523
 
    
 
3607
 
3524
3608
    def calculate_file_version_parents(self, text_key):
3525
3609
        """Calculate the correct parents for a file version according to
3526
3610
        the inventories.
3587
3671
        revision_graph[key] = tuple(parent for parent in parents if parent
3588
3672
            in revision_graph)
3589
3673
    return revision_graph
 
3674
 
 
3675
 
 
3676
class StreamSink(object):
 
3677
    """An object that can insert a stream into a repository.
 
3678
 
 
3679
    This interface handles the complexity of reserialising inventories and
 
3680
    revisions from different formats, and allows unidirectional insertion into
 
3681
    stacked repositories without looking for the missing basis parents
 
3682
    beforehand.
 
3683
    """
 
3684
 
 
3685
    def __init__(self, target_repo):
 
3686
        self.target_repo = target_repo
 
3687
 
 
3688
    def insert_stream(self, stream, src_format, resume_tokens):
 
3689
        """Insert a stream's content into the target repository.
 
3690
 
 
3691
        :param src_format: a bzr repository format.
 
3692
 
 
3693
        :return: a list of resume tokens and an  iterable of keys additional
 
3694
            items required before the insertion can be completed.
 
3695
        """
 
3696
        self.target_repo.lock_write()
 
3697
        try:
 
3698
            if resume_tokens:
 
3699
                self.target_repo.resume_write_group(resume_tokens)
 
3700
            else:
 
3701
                self.target_repo.start_write_group()
 
3702
            try:
 
3703
                # locked_insert_stream performs a commit|suspend.
 
3704
                return self._locked_insert_stream(stream, src_format)
 
3705
            except:
 
3706
                self.target_repo.abort_write_group(suppress_errors=True)
 
3707
                raise
 
3708
        finally:
 
3709
            self.target_repo.unlock()
 
3710
 
 
3711
    def _locked_insert_stream(self, stream, src_format):
 
3712
        to_serializer = self.target_repo._format._serializer
 
3713
        src_serializer = src_format._serializer
 
3714
        for substream_type, substream in stream:
 
3715
            if substream_type == 'texts':
 
3716
                self.target_repo.texts.insert_record_stream(substream)
 
3717
            elif substream_type == 'inventories':
 
3718
                if src_serializer == to_serializer:
 
3719
                    self.target_repo.inventories.insert_record_stream(
 
3720
                        substream)
 
3721
                else:
 
3722
                    self._extract_and_insert_inventories(
 
3723
                        substream, src_serializer)
 
3724
            elif substream_type == 'revisions':
 
3725
                # This may fallback to extract-and-insert more often than
 
3726
                # required if the serializers are different only in terms of
 
3727
                # the inventory.
 
3728
                if src_serializer == to_serializer:
 
3729
                    self.target_repo.revisions.insert_record_stream(
 
3730
                        substream)
 
3731
                else:
 
3732
                    self._extract_and_insert_revisions(substream,
 
3733
                        src_serializer)
 
3734
            elif substream_type == 'signatures':
 
3735
                self.target_repo.signatures.insert_record_stream(substream)
 
3736
            else:
 
3737
                raise AssertionError('kaboom! %s' % (substream_type,))
 
3738
        try:
 
3739
            missing_keys = set()
 
3740
            for prefix, versioned_file in (
 
3741
                ('texts', self.target_repo.texts),
 
3742
                ('inventories', self.target_repo.inventories),
 
3743
                ('revisions', self.target_repo.revisions),
 
3744
                ('signatures', self.target_repo.signatures),
 
3745
                ):
 
3746
                missing_keys.update((prefix,) + key for key in
 
3747
                    versioned_file.get_missing_compression_parent_keys())
 
3748
        except NotImplementedError:
 
3749
            # cannot even attempt suspending, and missing would have failed
 
3750
            # during stream insertion.
 
3751
            missing_keys = set()
 
3752
        else:
 
3753
            if missing_keys:
 
3754
                # suspend the write group and tell the caller what we is
 
3755
                # missing. We know we can suspend or else we would not have
 
3756
                # entered this code path. (All repositories that can handle
 
3757
                # missing keys can handle suspending a write group).
 
3758
                write_group_tokens = self.target_repo.suspend_write_group()
 
3759
                return write_group_tokens, missing_keys
 
3760
        self.target_repo.commit_write_group()
 
3761
        return [], set()
 
3762
 
 
3763
    def _extract_and_insert_inventories(self, substream, serializer):
 
3764
        """Generate a new inventory versionedfile in target, converting data.
 
3765
 
 
3766
        The inventory is retrieved from the source, (deserializing it), and
 
3767
        stored in the target (reserializing it in a different format).
 
3768
        """
 
3769
        for record in substream:
 
3770
            bytes = record.get_bytes_as('fulltext')
 
3771
            revision_id = record.key[0]
 
3772
            inv = serializer.read_inventory_from_string(bytes, revision_id)
 
3773
            parents = [key[0] for key in record.parents]
 
3774
            self.target_repo.add_inventory(revision_id, inv, parents)
 
3775
 
 
3776
    def _extract_and_insert_revisions(self, substream, serializer):
 
3777
        for record in substream:
 
3778
            bytes = record.get_bytes_as('fulltext')
 
3779
            revision_id = record.key[0]
 
3780
            rev = serializer.read_revision_from_string(bytes)
 
3781
            if rev.revision_id != revision_id:
 
3782
                raise AssertionError('wtf: %s != %s' % (rev, revision_id))
 
3783
            self.target_repo.add_revision(revision_id, rev)
 
3784
 
 
3785
    def finished(self):
 
3786
        if self.target_repo._fetch_reconcile:
 
3787
            self.target_repo.reconcile()
 
3788