~bzr/ubuntu/lucid/bzr/beta-ppa

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Martin Pool
  • Date: 2010-08-18 04:26:39 UTC
  • mfrom: (129.1.8 packaging-karmic)
  • Revision ID: mbp@sourcefrog.net-20100818042639-mjoxtngyjwiu05fo
* PPA rebuild for lucid.
* PPA rebuild for karmic.
* PPA rebuild onto jaunty.
* New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
    repository as _mod_repository,
31
31
    revision,
32
32
    revision as _mod_revision,
 
33
    static_tuple,
33
34
    symbol_versioning,
34
35
)
35
 
from bzrlib.branch import BranchReferenceFormat
 
36
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
36
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
37
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
38
39
from bzrlib.errors import (
42
43
from bzrlib.lockable_files import LockableFiles
43
44
from bzrlib.smart import client, vfs, repository as smart_repo
44
45
from bzrlib.revision import ensure_null, NULL_REVISION
 
46
from bzrlib.repository import RepositoryWriteLockResult
45
47
from bzrlib.trace import mutter, note, warning
46
48
 
47
49
 
243
245
        self._ensure_real()
244
246
        self._real_bzrdir.destroy_repository()
245
247
 
246
 
    def create_branch(self):
 
248
    def create_branch(self, name=None):
247
249
        # as per meta1 formats - just delegate to the format object which may
248
250
        # be parameterised.
249
 
        real_branch = self._format.get_branch_format().initialize(self)
 
251
        real_branch = self._format.get_branch_format().initialize(self,
 
252
            name=name)
250
253
        if not isinstance(real_branch, RemoteBranch):
251
 
            result = RemoteBranch(self, self.find_repository(), real_branch)
 
254
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
255
                                  name=name)
252
256
        else:
253
257
            result = real_branch
254
258
        # BzrDir.clone_on_transport() uses the result of create_branch but does
260
264
        self._next_open_branch_result = result
261
265
        return result
262
266
 
263
 
    def destroy_branch(self):
 
267
    def destroy_branch(self, name=None):
264
268
        """See BzrDir.destroy_branch"""
265
269
        self._ensure_real()
266
 
        self._real_bzrdir.destroy_branch()
 
270
        self._real_bzrdir.destroy_branch(name=name)
267
271
        self._next_open_branch_result = None
268
272
 
269
273
    def create_workingtree(self, revision_id=None, from_branch=None):
270
274
        raise errors.NotLocalUrl(self.transport.base)
271
275
 
272
 
    def find_branch_format(self):
 
276
    def find_branch_format(self, name=None):
273
277
        """Find the branch 'format' for this bzrdir.
274
278
 
275
279
        This might be a synthetic object for e.g. RemoteBranch and SVN.
276
280
        """
277
 
        b = self.open_branch()
 
281
        b = self.open_branch(name=name)
278
282
        return b._format
279
283
 
280
 
    def get_branch_reference(self):
 
284
    def get_branch_reference(self, name=None):
281
285
        """See BzrDir.get_branch_reference()."""
 
286
        if name is not None:
 
287
            # XXX JRV20100304: Support opening colocated branches
 
288
            raise errors.NoColocatedBranchSupport(self)
282
289
        response = self._get_branch_reference()
283
290
        if response[0] == 'ref':
284
291
            return response[1]
315
322
            raise errors.UnexpectedSmartServerResponse(response)
316
323
        return response
317
324
 
318
 
    def _get_tree_branch(self):
 
325
    def _get_tree_branch(self, name=None):
319
326
        """See BzrDir._get_tree_branch()."""
320
 
        return None, self.open_branch()
 
327
        return None, self.open_branch(name=name)
321
328
 
322
 
    def open_branch(self, _unsupported=False, ignore_fallbacks=False):
323
 
        if _unsupported:
 
329
    def open_branch(self, name=None, unsupported=False,
 
330
                    ignore_fallbacks=False):
 
331
        if unsupported:
324
332
            raise NotImplementedError('unsupported flag support not implemented yet.')
325
333
        if self._next_open_branch_result is not None:
326
334
            # See create_branch for details.
331
339
        if response[0] == 'ref':
332
340
            # a branch reference, use the existing BranchReference logic.
333
341
            format = BranchReferenceFormat()
334
 
            return format.open(self, _found=True, location=response[1],
335
 
                ignore_fallbacks=ignore_fallbacks)
 
342
            return format.open(self, name=name, _found=True,
 
343
                location=response[1], ignore_fallbacks=ignore_fallbacks)
336
344
        branch_format_name = response[1]
337
345
        if not branch_format_name:
338
346
            branch_format_name = None
339
347
        format = RemoteBranchFormat(network_name=branch_format_name)
340
348
        return RemoteBranch(self, self.find_repository(), format=format,
341
 
            setup_stacking=not ignore_fallbacks)
 
349
            setup_stacking=not ignore_fallbacks, name=name)
342
350
 
343
351
    def _open_repo_v1(self, path):
344
352
        verb = 'BzrDir.find_repository'
421
429
        """Return the path to be used for this bzrdir in a remote call."""
422
430
        return client.remote_path_from_transport(self.root_transport)
423
431
 
424
 
    def get_branch_transport(self, branch_format):
 
432
    def get_branch_transport(self, branch_format, name=None):
425
433
        self._ensure_real()
426
 
        return self._real_bzrdir.get_branch_transport(branch_format)
 
434
        return self._real_bzrdir.get_branch_transport(branch_format, name=name)
427
435
 
428
436
    def get_repository_transport(self, repository_format):
429
437
        self._ensure_real()
639
647
        return self._custom_format._serializer
640
648
 
641
649
 
642
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin):
 
650
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
 
651
    bzrdir.ControlComponent):
643
652
    """Repository accessed over rpc.
644
653
 
645
654
    For the moment most operations are performed using local transport-backed
688
697
        # Additional places to query for data.
689
698
        self._fallback_repositories = []
690
699
 
 
700
    @property
 
701
    def user_transport(self):
 
702
        return self.bzrdir.user_transport
 
703
 
 
704
    @property
 
705
    def control_transport(self):
 
706
        # XXX: Normally you shouldn't directly get at the remote repository
 
707
        # transport, but I'm not sure it's worth making this method
 
708
        # optional -- mbp 2010-04-21
 
709
        return self.bzrdir.get_repository_transport(None)
 
710
        
691
711
    def __str__(self):
692
712
        return "%s(%s)" % (self.__class__.__name__, self.base)
693
713
 
879
899
    def _has_same_fallbacks(self, other_repo):
880
900
        """Returns true if the repositories have the same fallbacks."""
881
901
        # XXX: copied from Repository; it should be unified into a base class
882
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
 
902
        # <https://bugs.launchpad.net/bzr/+bug/401622>
883
903
        my_fb = self._fallback_repositories
884
904
        other_fb = other_repo._fallback_repositories
885
905
        if len(my_fb) != len(other_fb):
901
921
        parents_provider = self._make_parents_provider(other_repository)
902
922
        return graph.Graph(parents_provider)
903
923
 
 
924
    @needs_read_lock
 
925
    def get_known_graph_ancestry(self, revision_ids):
 
926
        """Return the known graph for a set of revision ids and their ancestors.
 
927
        """
 
928
        st = static_tuple.StaticTuple
 
929
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
 
930
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
 
931
        return graph.GraphThunkIdsToKeys(known_graph)
 
932
 
904
933
    def gather_stats(self, revid=None, committers=None):
905
934
        """See Repository.gather_stats()."""
906
935
        path = self.bzrdir._path_for_remote_call(self._client)
972
1001
        pass
973
1002
 
974
1003
    def lock_read(self):
 
1004
        """Lock the repository for read operations.
 
1005
 
 
1006
        :return: A bzrlib.lock.LogicalLockResult.
 
1007
        """
975
1008
        # wrong eventually - want a local lock cache context
976
1009
        if not self._lock_mode:
977
1010
            self._note_lock('r')
984
1017
                repo.lock_read()
985
1018
        else:
986
1019
            self._lock_count += 1
 
1020
        return lock.LogicalLockResult(self.unlock)
987
1021
 
988
1022
    def _remote_lock_write(self, token):
989
1023
        path = self.bzrdir._path_for_remote_call(self._client)
1029
1063
            raise errors.ReadOnlyError(self)
1030
1064
        else:
1031
1065
            self._lock_count += 1
1032
 
        return self._lock_token or None
 
1066
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
1033
1067
 
1034
1068
    def leave_lock_in_place(self):
1035
1069
        if not self._lock_token:
1221
1255
        # _real_branch had its get_stacked_on_url method called), then the
1222
1256
        # repository to be added may already be in the _real_repositories list.
1223
1257
        if self._real_repository is not None:
1224
 
            fallback_locations = [repo.bzrdir.root_transport.base for repo in
 
1258
            fallback_locations = [repo.user_url for repo in
1225
1259
                self._real_repository._fallback_repositories]
1226
 
            if repository.bzrdir.root_transport.base not in fallback_locations:
 
1260
            if repository.user_url not in fallback_locations:
1227
1261
                self._real_repository.add_fallback_repository(repository)
1228
1262
 
1229
1263
    def _check_fallback_repository(self, repository):
1241
1275
        return self._real_repository.add_inventory(revid, inv, parents)
1242
1276
 
1243
1277
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1244
 
                               parents):
 
1278
            parents, basis_inv=None, propagate_caches=False):
1245
1279
        self._ensure_real()
1246
1280
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
1247
 
            delta, new_revision_id, parents)
 
1281
            delta, new_revision_id, parents, basis_inv=basis_inv,
 
1282
            propagate_caches=propagate_caches)
1248
1283
 
1249
1284
    def add_revision(self, rev_id, rev, inv=None, config=None):
1250
1285
        self._ensure_real()
1280
1315
        return self._real_repository.make_working_trees()
1281
1316
 
1282
1317
    def refresh_data(self):
1283
 
        """Re-read any data needed to to synchronise with disk.
 
1318
        """Re-read any data needed to synchronise with disk.
1284
1319
 
1285
1320
        This method is intended to be called after another repository instance
1286
1321
        (such as one used by a smart server) has inserted data into the
1287
 
        repository. It may not be called during a write group, but may be
1288
 
        called at any other time.
 
1322
        repository. On all repositories this will work outside of write groups.
 
1323
        Some repository formats (pack and newer for bzrlib native formats)
 
1324
        support refresh_data inside write groups. If called inside a write
 
1325
        group on a repository that does not support refreshing in a write group
 
1326
        IsInWriteGroupError will be raised.
1289
1327
        """
1290
 
        if self.is_in_write_group():
1291
 
            raise errors.InternalBzrError(
1292
 
                "May not refresh_data while in a write group.")
1293
1328
        if self._real_repository is not None:
1294
1329
            self._real_repository.refresh_data()
1295
1330
 
1509
1544
        return self._real_repository.get_signature_text(revision_id)
1510
1545
 
1511
1546
    @needs_read_lock
1512
 
    def get_inventory_xml(self, revision_id):
1513
 
        self._ensure_real()
1514
 
        return self._real_repository.get_inventory_xml(revision_id)
1515
 
 
1516
 
    def deserialise_inventory(self, revision_id, xml):
1517
 
        self._ensure_real()
1518
 
        return self._real_repository.deserialise_inventory(revision_id, xml)
 
1547
    def _get_inventory_xml(self, revision_id):
 
1548
        self._ensure_real()
 
1549
        return self._real_repository._get_inventory_xml(revision_id)
1519
1550
 
1520
1551
    def reconcile(self, other=None, thorough=False):
1521
1552
        self._ensure_real()
1597
1628
        return self._real_repository.inventories
1598
1629
 
1599
1630
    @needs_write_lock
1600
 
    def pack(self, hint=None):
 
1631
    def pack(self, hint=None, clean_obsolete_packs=False):
1601
1632
        """Compress the data within the repository.
1602
1633
 
1603
1634
        This is not currently implemented within the smart server.
1604
1635
        """
1605
1636
        self._ensure_real()
1606
 
        return self._real_repository.pack(hint=hint)
 
1637
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
1607
1638
 
1608
1639
    @property
1609
1640
    def revisions(self):
1949
1980
        if response_tuple[0] != 'ok':
1950
1981
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1951
1982
        byte_stream = response_handler.read_streamed_body()
1952
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
 
1983
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
 
1984
            self._record_counter)
1953
1985
        if src_format.network_name() != repo._format.network_name():
1954
1986
            raise AssertionError(
1955
1987
                "Mismatched RemoteRepository and stream src %r, %r" % (
2037
2069
    def network_name(self):
2038
2070
        return self._network_name
2039
2071
 
2040
 
    def open(self, a_bzrdir, ignore_fallbacks=False):
2041
 
        return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
 
2072
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
 
2073
        return a_bzrdir.open_branch(name=name, 
 
2074
            ignore_fallbacks=ignore_fallbacks)
2042
2075
 
2043
 
    def _vfs_initialize(self, a_bzrdir):
 
2076
    def _vfs_initialize(self, a_bzrdir, name):
2044
2077
        # Initialisation when using a local bzrdir object, or a non-vfs init
2045
2078
        # method is not available on the server.
2046
2079
        # self._custom_format is always set - the start of initialize ensures
2047
2080
        # that.
2048
2081
        if isinstance(a_bzrdir, RemoteBzrDir):
2049
2082
            a_bzrdir._ensure_real()
2050
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
 
2083
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
 
2084
                name)
2051
2085
        else:
2052
2086
            # We assume the bzrdir is parameterised; it may not be.
2053
 
            result = self._custom_format.initialize(a_bzrdir)
 
2087
            result = self._custom_format.initialize(a_bzrdir, name)
2054
2088
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2055
2089
            not isinstance(result, RemoteBranch)):
2056
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
 
2090
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
 
2091
                                  name=name)
2057
2092
        return result
2058
2093
 
2059
 
    def initialize(self, a_bzrdir):
 
2094
    def initialize(self, a_bzrdir, name=None):
2060
2095
        # 1) get the network name to use.
2061
2096
        if self._custom_format:
2062
2097
            network_name = self._custom_format.network_name()
2068
2103
            network_name = reference_format.network_name()
2069
2104
        # Being asked to create on a non RemoteBzrDir:
2070
2105
        if not isinstance(a_bzrdir, RemoteBzrDir):
2071
 
            return self._vfs_initialize(a_bzrdir)
 
2106
            return self._vfs_initialize(a_bzrdir, name=name)
2072
2107
        medium = a_bzrdir._client._medium
2073
2108
        if medium._is_remote_before((1, 13)):
2074
 
            return self._vfs_initialize(a_bzrdir)
 
2109
            return self._vfs_initialize(a_bzrdir, name=name)
2075
2110
        # Creating on a remote bzr dir.
2076
2111
        # 2) try direct creation via RPC
2077
2112
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
2113
        if name is not None:
 
2114
            # XXX JRV20100304: Support creating colocated branches
 
2115
            raise errors.NoColocatedBranchSupport(self)
2078
2116
        verb = 'BzrDir.create_branch'
2079
2117
        try:
2080
2118
            response = a_bzrdir._call(verb, path, network_name)
2081
2119
        except errors.UnknownSmartMethod:
2082
2120
            # Fallback - use vfs methods
2083
2121
            medium._remember_remote_is_before((1, 13))
2084
 
            return self._vfs_initialize(a_bzrdir)
 
2122
            return self._vfs_initialize(a_bzrdir, name=name)
2085
2123
        if response[0] != 'ok':
2086
2124
            raise errors.UnexpectedSmartServerResponse(response)
2087
2125
        # Turn the response into a RemoteRepository object.
2095
2133
                a_bzrdir._client)
2096
2134
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2097
2135
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2098
 
            format=format, setup_stacking=False)
 
2136
            format=format, setup_stacking=False, name=name)
2099
2137
        # XXX: We know this is a new branch, so it must have revno 0, revid
2100
2138
        # NULL_REVISION. Creating the branch locked would make this be unable
2101
2139
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2128
2166
    """
2129
2167
 
2130
2168
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2131
 
        _client=None, format=None, setup_stacking=True):
 
2169
        _client=None, format=None, setup_stacking=True, name=None):
2132
2170
        """Create a RemoteBranch instance.
2133
2171
 
2134
2172
        :param real_branch: An optional local implementation of the branch
2140
2178
        :param setup_stacking: If True make an RPC call to determine the
2141
2179
            stacked (or not) status of the branch. If False assume the branch
2142
2180
            is not stacked.
 
2181
        :param name: Colocated branch name
2143
2182
        """
2144
2183
        # We intentionally don't call the parent class's __init__, because it
2145
2184
        # will try to assign to self.tags, which is a property in this subclass.
2164
2203
            self._real_branch = None
2165
2204
        # Fill out expected attributes of branch for bzrlib API users.
2166
2205
        self._clear_cached_state()
2167
 
        self.base = self.bzrdir.root_transport.base
 
2206
        # TODO: deprecate self.base in favor of user_url
 
2207
        self.base = self.bzrdir.user_url
 
2208
        self._name = name
2168
2209
        self._control_files = None
2169
2210
        self._lock_mode = None
2170
2211
        self._lock_token = None
2235
2276
                    'to use vfs implementation')
2236
2277
            self.bzrdir._ensure_real()
2237
2278
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2238
 
                ignore_fallbacks=self._real_ignore_fallbacks)
 
2279
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
2239
2280
            if self.repository._real_repository is None:
2240
2281
                # Give the remote repository the matching real repo.
2241
2282
                real_repo = self._real_branch.repository
2356
2397
            self._vfs_set_tags_bytes(bytes)
2357
2398
 
2358
2399
    def lock_read(self):
 
2400
        """Lock the branch for read operations.
 
2401
 
 
2402
        :return: A bzrlib.lock.LogicalLockResult.
 
2403
        """
2359
2404
        self.repository.lock_read()
2360
2405
        if not self._lock_mode:
2361
2406
            self._note_lock('r')
2365
2410
                self._real_branch.lock_read()
2366
2411
        else:
2367
2412
            self._lock_count += 1
 
2413
        return lock.LogicalLockResult(self.unlock)
2368
2414
 
2369
2415
    def _remote_lock_write(self, token):
2370
2416
        if token is None:
2371
2417
            branch_token = repo_token = ''
2372
2418
        else:
2373
2419
            branch_token = token
2374
 
            repo_token = self.repository.lock_write()
 
2420
            repo_token = self.repository.lock_write().repository_token
2375
2421
            self.repository.unlock()
2376
2422
        err_context = {'token': token}
2377
 
        response = self._call(
2378
 
            'Branch.lock_write', self._remote_path(), branch_token,
2379
 
            repo_token or '', **err_context)
 
2423
        try:
 
2424
            response = self._call(
 
2425
                'Branch.lock_write', self._remote_path(), branch_token,
 
2426
                repo_token or '', **err_context)
 
2427
        except errors.LockContention, e:
 
2428
            # The LockContention from the server doesn't have any
 
2429
            # information about the lock_url. We re-raise LockContention
 
2430
            # with valid lock_url.
 
2431
            raise errors.LockContention('(remote lock)',
 
2432
                self.repository.base.split('.bzr/')[0])
2380
2433
        if response[0] != 'ok':
2381
2434
            raise errors.UnexpectedSmartServerResponse(response)
2382
2435
        ok, branch_token, repo_token = response
2403
2456
            self._lock_mode = 'w'
2404
2457
            self._lock_count = 1
2405
2458
        elif self._lock_mode == 'r':
2406
 
            raise errors.ReadOnlyTransaction
 
2459
            raise errors.ReadOnlyError(self)
2407
2460
        else:
2408
2461
            if token is not None:
2409
2462
                # A token was given to lock_write, and we're relocking, so
2414
2467
            self._lock_count += 1
2415
2468
            # Re-lock the repository too.
2416
2469
            self.repository.lock_write(self._repo_lock_token)
2417
 
        return self._lock_token or None
 
2470
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
2418
2471
 
2419
2472
    def _unlock(self, branch_token, repo_token):
2420
2473
        err_context = {'token': str((branch_token, repo_token))}
2743
2796
        medium = self._branch._client._medium
2744
2797
        if medium._is_remote_before((1, 14)):
2745
2798
            return self._vfs_set_option(value, name, section)
 
2799
        if isinstance(value, dict):
 
2800
            if medium._is_remote_before((2, 2)):
 
2801
                return self._vfs_set_option(value, name, section)
 
2802
            return self._set_config_option_dict(value, name, section)
 
2803
        else:
 
2804
            return self._set_config_option(value, name, section)
 
2805
 
 
2806
    def _set_config_option(self, value, name, section):
2746
2807
        try:
2747
2808
            path = self._branch._remote_path()
2748
2809
            response = self._branch._client.call('Branch.set_config_option',
2749
2810
                path, self._branch._lock_token, self._branch._repo_lock_token,
2750
2811
                value.encode('utf8'), name, section or '')
2751
2812
        except errors.UnknownSmartMethod:
 
2813
            medium = self._branch._client._medium
2752
2814
            medium._remember_remote_is_before((1, 14))
2753
2815
            return self._vfs_set_option(value, name, section)
2754
2816
        if response != ():
2755
2817
            raise errors.UnexpectedSmartServerResponse(response)
2756
2818
 
 
2819
    def _serialize_option_dict(self, option_dict):
 
2820
        utf8_dict = {}
 
2821
        for key, value in option_dict.items():
 
2822
            if isinstance(key, unicode):
 
2823
                key = key.encode('utf8')
 
2824
            if isinstance(value, unicode):
 
2825
                value = value.encode('utf8')
 
2826
            utf8_dict[key] = value
 
2827
        return bencode.bencode(utf8_dict)
 
2828
 
 
2829
    def _set_config_option_dict(self, value, name, section):
 
2830
        try:
 
2831
            path = self._branch._remote_path()
 
2832
            serialised_dict = self._serialize_option_dict(value)
 
2833
            response = self._branch._client.call(
 
2834
                'Branch.set_config_option_dict',
 
2835
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
2836
                serialised_dict, name, section or '')
 
2837
        except errors.UnknownSmartMethod:
 
2838
            medium = self._branch._client._medium
 
2839
            medium._remember_remote_is_before((2, 2))
 
2840
            return self._vfs_set_option(value, name, section)
 
2841
        if response != ():
 
2842
            raise errors.UnexpectedSmartServerResponse(response)
 
2843
 
2757
2844
    def _real_object(self):
2758
2845
        self._branch._ensure_real()
2759
2846
        return self._branch._real_branch