~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-08-15 09:15:03 UTC
  • mfrom: (4595.7.4 409137-lsb-release)
  • Revision ID: pqm@pqm.ubuntu.com-20090815091503-qwbm6glvv31rnujw
(mbp) show platform in selftest, version and backtrace

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
21
21
    branch,
22
22
    bzrdir,
23
23
    config,
24
 
    controldir,
25
24
    debug,
26
25
    errors,
27
26
    graph,
28
27
    lock,
29
28
    lockdir,
30
29
    repository,
31
 
    repository as _mod_repository,
32
30
    revision,
33
31
    revision as _mod_revision,
34
 
    static_tuple,
35
32
    symbol_versioning,
36
33
)
37
 
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
34
from bzrlib.branch import BranchReferenceFormat
38
35
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
39
 
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
36
from bzrlib.decorators import needs_read_lock, needs_write_lock
40
37
from bzrlib.errors import (
41
38
    NoSuchRevision,
42
39
    SmartProtocolError,
44
41
from bzrlib.lockable_files import LockableFiles
45
42
from bzrlib.smart import client, vfs, repository as smart_repo
46
43
from bzrlib.revision import ensure_null, NULL_REVISION
47
 
from bzrlib.repository import RepositoryWriteLockResult
48
44
from bzrlib.trace import mutter, note, warning
49
45
 
50
46
 
93
89
class RemoteBzrDir(BzrDir, _RpcHelper):
94
90
    """Control directory on a remote server, accessed via bzr:// or similar."""
95
91
 
96
 
    def __init__(self, transport, format, _client=None, _force_probe=False):
 
92
    def __init__(self, transport, format, _client=None):
97
93
        """Construct a RemoteBzrDir.
98
94
 
99
95
        :param _client: Private parameter for testing. Disables probing and the
103
99
        # this object holds a delegated bzrdir that uses file-level operations
104
100
        # to talk to the other side
105
101
        self._real_bzrdir = None
106
 
        self._has_working_tree = None
107
102
        # 1-shot cache for the call pattern 'create_branch; open_branch' - see
108
103
        # create_branch for details.
109
104
        self._next_open_branch_result = None
113
108
            self._client = client._SmartClient(medium)
114
109
        else:
115
110
            self._client = _client
116
 
            if not _force_probe:
117
 
                return
118
 
 
119
 
        self._probe_bzrdir()
120
 
 
121
 
    def __repr__(self):
122
 
        return '%s(%r)' % (self.__class__.__name__, self._client)
123
 
 
124
 
    def _probe_bzrdir(self):
125
 
        medium = self._client._medium
 
111
            return
 
112
 
126
113
        path = self._path_for_remote_call(self._client)
127
 
        if medium._is_remote_before((2, 1)):
128
 
            self._rpc_open(path)
129
 
            return
130
 
        try:
131
 
            self._rpc_open_2_1(path)
132
 
            return
133
 
        except errors.UnknownSmartMethod:
134
 
            medium._remember_remote_is_before((2, 1))
135
 
            self._rpc_open(path)
136
 
 
137
 
    def _rpc_open_2_1(self, path):
138
 
        response = self._call('BzrDir.open_2.1', path)
139
 
        if response == ('no',):
140
 
            raise errors.NotBranchError(path=self.root_transport.base)
141
 
        elif response[0] == 'yes':
142
 
            if response[1] == 'yes':
143
 
                self._has_working_tree = True
144
 
            elif response[1] == 'no':
145
 
                self._has_working_tree = False
146
 
            else:
147
 
                raise errors.UnexpectedSmartServerResponse(response)
148
 
        else:
149
 
            raise errors.UnexpectedSmartServerResponse(response)
150
 
 
151
 
    def _rpc_open(self, path):
152
114
        response = self._call('BzrDir.open', path)
153
115
        if response not in [('yes',), ('no',)]:
154
116
            raise errors.UnexpectedSmartServerResponse(response)
155
117
        if response == ('no',):
156
 
            raise errors.NotBranchError(path=self.root_transport.base)
 
118
            raise errors.NotBranchError(path=transport.base)
157
119
 
158
120
    def _ensure_real(self):
159
121
        """Ensure that there is a _real_bzrdir set.
161
123
        Used before calls to self._real_bzrdir.
162
124
        """
163
125
        if not self._real_bzrdir:
164
 
            if 'hpssvfs' in debug.debug_flags:
165
 
                import traceback
166
 
                warning('VFS BzrDir access triggered\n%s',
167
 
                    ''.join(traceback.format_stack()))
168
126
            self._real_bzrdir = BzrDir.open_from_transport(
169
127
                self.root_transport, _server_formats=False)
170
128
            self._format._network_name = \
246
204
        self._ensure_real()
247
205
        self._real_bzrdir.destroy_repository()
248
206
 
249
 
    def create_branch(self, name=None):
 
207
    def create_branch(self):
250
208
        # as per meta1 formats - just delegate to the format object which may
251
209
        # be parameterised.
252
 
        real_branch = self._format.get_branch_format().initialize(self,
253
 
            name=name)
 
210
        real_branch = self._format.get_branch_format().initialize(self)
254
211
        if not isinstance(real_branch, RemoteBranch):
255
 
            result = RemoteBranch(self, self.find_repository(), real_branch,
256
 
                                  name=name)
 
212
            result = RemoteBranch(self, self.find_repository(), real_branch)
257
213
        else:
258
214
            result = real_branch
259
215
        # BzrDir.clone_on_transport() uses the result of create_branch but does
265
221
        self._next_open_branch_result = result
266
222
        return result
267
223
 
268
 
    def destroy_branch(self, name=None):
 
224
    def destroy_branch(self):
269
225
        """See BzrDir.destroy_branch"""
270
226
        self._ensure_real()
271
 
        self._real_bzrdir.destroy_branch(name=name)
 
227
        self._real_bzrdir.destroy_branch()
272
228
        self._next_open_branch_result = None
273
229
 
274
230
    def create_workingtree(self, revision_id=None, from_branch=None):
275
231
        raise errors.NotLocalUrl(self.transport.base)
276
232
 
277
 
    def find_branch_format(self, name=None):
 
233
    def find_branch_format(self):
278
234
        """Find the branch 'format' for this bzrdir.
279
235
 
280
236
        This might be a synthetic object for e.g. RemoteBranch and SVN.
281
237
        """
282
 
        b = self.open_branch(name=name)
 
238
        b = self.open_branch()
283
239
        return b._format
284
240
 
285
 
    def get_branch_reference(self, name=None):
 
241
    def get_branch_reference(self):
286
242
        """See BzrDir.get_branch_reference()."""
287
 
        if name is not None:
288
 
            # XXX JRV20100304: Support opening colocated branches
289
 
            raise errors.NoColocatedBranchSupport(self)
290
243
        response = self._get_branch_reference()
291
244
        if response[0] == 'ref':
292
245
            return response[1]
296
249
    def _get_branch_reference(self):
297
250
        path = self._path_for_remote_call(self._client)
298
251
        medium = self._client._medium
299
 
        candidate_calls = [
300
 
            ('BzrDir.open_branchV3', (2, 1)),
301
 
            ('BzrDir.open_branchV2', (1, 13)),
302
 
            ('BzrDir.open_branch', None),
303
 
            ]
304
 
        for verb, required_version in candidate_calls:
305
 
            if required_version and medium._is_remote_before(required_version):
306
 
                continue
 
252
        if not medium._is_remote_before((1, 13)):
307
253
            try:
308
 
                response = self._call(verb, path)
 
254
                response = self._call('BzrDir.open_branchV2', path)
 
255
                if response[0] not in ('ref', 'branch'):
 
256
                    raise errors.UnexpectedSmartServerResponse(response)
 
257
                return response
309
258
            except errors.UnknownSmartMethod:
310
 
                if required_version is None:
311
 
                    raise
312
 
                medium._remember_remote_is_before(required_version)
313
 
            else:
314
 
                break
315
 
        if verb == 'BzrDir.open_branch':
316
 
            if response[0] != 'ok':
317
 
                raise errors.UnexpectedSmartServerResponse(response)
318
 
            if response[1] != '':
319
 
                return ('ref', response[1])
320
 
            else:
321
 
                return ('branch', '')
322
 
        if response[0] not in ('ref', 'branch'):
 
259
                medium._remember_remote_is_before((1, 13))
 
260
        response = self._call('BzrDir.open_branch', path)
 
261
        if response[0] != 'ok':
323
262
            raise errors.UnexpectedSmartServerResponse(response)
324
 
        return response
 
263
        if response[1] != '':
 
264
            return ('ref', response[1])
 
265
        else:
 
266
            return ('branch', '')
325
267
 
326
 
    def _get_tree_branch(self, name=None):
 
268
    def _get_tree_branch(self):
327
269
        """See BzrDir._get_tree_branch()."""
328
 
        return None, self.open_branch(name=name)
 
270
        return None, self.open_branch()
329
271
 
330
 
    def open_branch(self, name=None, unsupported=False,
331
 
                    ignore_fallbacks=False):
332
 
        if unsupported:
 
272
    def open_branch(self, _unsupported=False, ignore_fallbacks=False):
 
273
        if _unsupported:
333
274
            raise NotImplementedError('unsupported flag support not implemented yet.')
334
275
        if self._next_open_branch_result is not None:
335
276
            # See create_branch for details.
340
281
        if response[0] == 'ref':
341
282
            # a branch reference, use the existing BranchReference logic.
342
283
            format = BranchReferenceFormat()
343
 
            return format.open(self, name=name, _found=True,
344
 
                location=response[1], ignore_fallbacks=ignore_fallbacks)
 
284
            return format.open(self, _found=True, location=response[1],
 
285
                ignore_fallbacks=ignore_fallbacks)
345
286
        branch_format_name = response[1]
346
287
        if not branch_format_name:
347
288
            branch_format_name = None
348
289
        format = RemoteBranchFormat(network_name=branch_format_name)
349
290
        return RemoteBranch(self, self.find_repository(), format=format,
350
 
            setup_stacking=not ignore_fallbacks, name=name)
 
291
            setup_stacking=not ignore_fallbacks)
351
292
 
352
293
    def _open_repo_v1(self, path):
353
294
        verb = 'BzrDir.find_repository'
414
355
        else:
415
356
            raise errors.NoRepositoryPresent(self)
416
357
 
417
 
    def has_workingtree(self):
418
 
        if self._has_working_tree is None:
419
 
            self._ensure_real()
420
 
            self._has_working_tree = self._real_bzrdir.has_workingtree()
421
 
        return self._has_working_tree
422
 
 
423
358
    def open_workingtree(self, recommend_upgrade=True):
424
 
        if self.has_workingtree():
 
359
        self._ensure_real()
 
360
        if self._real_bzrdir.has_workingtree():
425
361
            raise errors.NotLocalUrl(self.root_transport)
426
362
        else:
427
363
            raise errors.NoWorkingTree(self.root_transport.base)
430
366
        """Return the path to be used for this bzrdir in a remote call."""
431
367
        return client.remote_path_from_transport(self.root_transport)
432
368
 
433
 
    def get_branch_transport(self, branch_format, name=None):
 
369
    def get_branch_transport(self, branch_format):
434
370
        self._ensure_real()
435
 
        return self._real_bzrdir.get_branch_transport(branch_format, name=name)
 
371
        return self._real_bzrdir.get_branch_transport(branch_format)
436
372
 
437
373
    def get_repository_transport(self, repository_format):
438
374
        self._ensure_real()
625
561
        return self._custom_format._fetch_reconcile
626
562
 
627
563
    def get_format_description(self):
628
 
        self._ensure_real()
629
 
        return 'Remote: ' + self._custom_format.get_format_description()
 
564
        return 'bzr remote repository'
630
565
 
631
566
    def __eq__(self, other):
632
567
        return self.__class__ is other.__class__
633
568
 
 
569
    def check_conversion_target(self, target_format):
 
570
        if self.rich_root_data and not target_format.rich_root_data:
 
571
            raise errors.BadConversionTarget(
 
572
                'Does not support rich root data.', target_format,
 
573
                from_format=self)
 
574
        if (self.supports_tree_reference and
 
575
            not getattr(target_format, 'supports_tree_reference', False)):
 
576
            raise errors.BadConversionTarget(
 
577
                'Does not support nested trees', target_format,
 
578
                from_format=self)
 
579
 
634
580
    def network_name(self):
635
581
        if self._network_name:
636
582
            return self._network_name
648
594
        return self._custom_format._serializer
649
595
 
650
596
 
651
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
652
 
    controldir.ControlComponent):
 
597
class RemoteRepository(_RpcHelper):
653
598
    """Repository accessed over rpc.
654
599
 
655
600
    For the moment most operations are performed using local transport-backed
698
643
        # Additional places to query for data.
699
644
        self._fallback_repositories = []
700
645
 
701
 
    @property
702
 
    def user_transport(self):
703
 
        return self.bzrdir.user_transport
704
 
 
705
 
    @property
706
 
    def control_transport(self):
707
 
        # XXX: Normally you shouldn't directly get at the remote repository
708
 
        # transport, but I'm not sure it's worth making this method
709
 
        # optional -- mbp 2010-04-21
710
 
        return self.bzrdir.get_repository_transport(None)
711
 
        
712
646
    def __str__(self):
713
647
        return "%s(%s)" % (self.__class__.__name__, self.base)
714
648
 
900
834
    def _has_same_fallbacks(self, other_repo):
901
835
        """Returns true if the repositories have the same fallbacks."""
902
836
        # XXX: copied from Repository; it should be unified into a base class
903
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
837
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
904
838
        my_fb = self._fallback_repositories
905
839
        other_fb = other_repo._fallback_repositories
906
840
        if len(my_fb) != len(other_fb):
922
856
        parents_provider = self._make_parents_provider(other_repository)
923
857
        return graph.Graph(parents_provider)
924
858
 
925
 
    @needs_read_lock
926
 
    def get_known_graph_ancestry(self, revision_ids):
927
 
        """Return the known graph for a set of revision ids and their ancestors.
928
 
        """
929
 
        st = static_tuple.StaticTuple
930
 
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
931
 
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
932
 
        return graph.GraphThunkIdsToKeys(known_graph)
933
 
 
934
859
    def gather_stats(self, revid=None, committers=None):
935
860
        """See Repository.gather_stats()."""
936
861
        path = self.bzrdir._path_for_remote_call(self._client)
996
921
    def is_write_locked(self):
997
922
        return self._lock_mode == 'w'
998
923
 
999
 
    def _warn_if_deprecated(self, branch=None):
1000
 
        # If we have a real repository, the check will be done there, if we
1001
 
        # don't the check will be done remotely.
1002
 
        pass
1003
 
 
1004
924
    def lock_read(self):
1005
 
        """Lock the repository for read operations.
1006
 
 
1007
 
        :return: A bzrlib.lock.LogicalLockResult.
1008
 
        """
1009
925
        # wrong eventually - want a local lock cache context
1010
926
        if not self._lock_mode:
1011
 
            self._note_lock('r')
1012
927
            self._lock_mode = 'r'
1013
928
            self._lock_count = 1
1014
929
            self._unstacked_provider.enable_cache(cache_misses=True)
1018
933
                repo.lock_read()
1019
934
        else:
1020
935
            self._lock_count += 1
1021
 
        return lock.LogicalLockResult(self.unlock)
1022
936
 
1023
937
    def _remote_lock_write(self, token):
1024
938
        path = self.bzrdir._path_for_remote_call(self._client)
1035
949
 
1036
950
    def lock_write(self, token=None, _skip_rpc=False):
1037
951
        if not self._lock_mode:
1038
 
            self._note_lock('w')
1039
952
            if _skip_rpc:
1040
953
                if self._lock_token is not None:
1041
954
                    if token != self._lock_token:
1064
977
            raise errors.ReadOnlyError(self)
1065
978
        else:
1066
979
            self._lock_count += 1
1067
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
980
        return self._lock_token or None
1068
981
 
1069
982
    def leave_lock_in_place(self):
1070
983
        if not self._lock_token:
1144
1057
        else:
1145
1058
            raise errors.UnexpectedSmartServerResponse(response)
1146
1059
 
1147
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
1148
1060
    def unlock(self):
1149
1061
        if not self._lock_count:
1150
1062
            return lock.cant_unlock_not_held(self)
1250
1162
            # state, so always add a lock here. If a caller passes us a locked
1251
1163
            # repository, they are responsible for unlocking it later.
1252
1164
            repository.lock_read()
1253
 
        self._check_fallback_repository(repository)
1254
1165
        self._fallback_repositories.append(repository)
1255
1166
        # If self._real_repository was parameterised already (e.g. because a
1256
1167
        # _real_branch had its get_stacked_on_url method called), then the
1257
1168
        # repository to be added may already be in the _real_repositories list.
1258
1169
        if self._real_repository is not None:
1259
 
            fallback_locations = [repo.user_url for repo in
 
1170
            fallback_locations = [repo.bzrdir.root_transport.base for repo in
1260
1171
                self._real_repository._fallback_repositories]
1261
 
            if repository.user_url not in fallback_locations:
 
1172
            if repository.bzrdir.root_transport.base not in fallback_locations:
1262
1173
                self._real_repository.add_fallback_repository(repository)
1263
1174
 
1264
 
    def _check_fallback_repository(self, repository):
1265
 
        """Check that this repository can fallback to repository safely.
1266
 
 
1267
 
        Raise an error if not.
1268
 
 
1269
 
        :param repository: A repository to fallback to.
1270
 
        """
1271
 
        return _mod_repository.InterRepository._assert_same_model(
1272
 
            self, repository)
1273
 
 
1274
1175
    def add_inventory(self, revid, inv, parents):
1275
1176
        self._ensure_real()
1276
1177
        return self._real_repository.add_inventory(revid, inv, parents)
1277
1178
 
1278
1179
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1279
 
            parents, basis_inv=None, propagate_caches=False):
 
1180
                               parents):
1280
1181
        self._ensure_real()
1281
1182
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
1282
 
            delta, new_revision_id, parents, basis_inv=basis_inv,
1283
 
            propagate_caches=propagate_caches)
 
1183
            delta, new_revision_id, parents)
1284
1184
 
1285
1185
    def add_revision(self, rev_id, rev, inv=None, config=None):
1286
1186
        self._ensure_real()
1316
1216
        return self._real_repository.make_working_trees()
1317
1217
 
1318
1218
    def refresh_data(self):
1319
 
        """Re-read any data needed to synchronise with disk.
 
1219
        """Re-read any data needed to to synchronise with disk.
1320
1220
 
1321
1221
        This method is intended to be called after another repository instance
1322
1222
        (such as one used by a smart server) has inserted data into the
1323
 
        repository. On all repositories this will work outside of write groups.
1324
 
        Some repository formats (pack and newer for bzrlib native formats)
1325
 
        support refresh_data inside write groups. If called inside a write
1326
 
        group on a repository that does not support refreshing in a write group
1327
 
        IsInWriteGroupError will be raised.
 
1223
        repository. It may not be called during a write group, but may be
 
1224
        called at any other time.
1328
1225
        """
 
1226
        if self.is_in_write_group():
 
1227
            raise errors.InternalBzrError(
 
1228
                "May not refresh_data while in a write group.")
1329
1229
        if self._real_repository is not None:
1330
1230
            self._real_repository.refresh_data()
1331
1231
 
1545
1445
        return self._real_repository.get_signature_text(revision_id)
1546
1446
 
1547
1447
    @needs_read_lock
1548
 
    def _get_inventory_xml(self, revision_id):
1549
 
        self._ensure_real()
1550
 
        return self._real_repository._get_inventory_xml(revision_id)
 
1448
    def get_inventory_xml(self, revision_id):
 
1449
        self._ensure_real()
 
1450
        return self._real_repository.get_inventory_xml(revision_id)
 
1451
 
 
1452
    def deserialise_inventory(self, revision_id, xml):
 
1453
        self._ensure_real()
 
1454
        return self._real_repository.deserialise_inventory(revision_id, xml)
1551
1455
 
1552
1456
    def reconcile(self, other=None, thorough=False):
1553
1457
        self._ensure_real()
1629
1533
        return self._real_repository.inventories
1630
1534
 
1631
1535
    @needs_write_lock
1632
 
    def pack(self, hint=None, clean_obsolete_packs=False):
 
1536
    def pack(self, hint=None):
1633
1537
        """Compress the data within the repository.
1634
1538
 
1635
1539
        This is not currently implemented within the smart server.
1636
1540
        """
1637
1541
        self._ensure_real()
1638
 
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
 
1542
        return self._real_repository.pack(hint=hint)
1639
1543
 
1640
1544
    @property
1641
1545
    def revisions(self):
1846
1750
            # The stream included an inventory-delta record, but the remote
1847
1751
            # side isn't new enough to support them.  So we need to send the
1848
1752
            # rest of the stream via VFS.
1849
 
            self.target_repo.refresh_data()
1850
1753
            return self._resume_stream_with_vfs(response, src_format)
1851
1754
        if response[0][0] == 'missing-basis':
1852
1755
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1981
1884
        if response_tuple[0] != 'ok':
1982
1885
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1983
1886
        byte_stream = response_handler.read_streamed_body()
1984
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
1985
 
            self._record_counter)
 
1887
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
1986
1888
        if src_format.network_name() != repo._format.network_name():
1987
1889
            raise AssertionError(
1988
1890
                "Mismatched RemoteRepository and stream src %r, %r" % (
1995
1897
        :param search: The overall search to satisfy with streams.
1996
1898
        :param sources: A list of Repository objects to query.
1997
1899
        """
1998
 
        self.from_serialiser = self.from_repository._format._serializer
 
1900
        self.serialiser = self.to_format._serializer
1999
1901
        self.seen_revs = set()
2000
1902
        self.referenced_revs = set()
2001
1903
        # If there are heads in the search, or the key count is > 0, we are not
2018
1920
    def missing_parents_rev_handler(self, substream):
2019
1921
        for content in substream:
2020
1922
            revision_bytes = content.get_bytes_as('fulltext')
2021
 
            revision = self.from_serialiser.read_revision_from_string(
2022
 
                revision_bytes)
 
1923
            revision = self.serialiser.read_revision_from_string(revision_bytes)
2023
1924
            self.seen_revs.add(content.key[-1])
2024
1925
            self.referenced_revs.update(revision.parent_ids)
2025
1926
            yield content
2064
1965
                self._network_name)
2065
1966
 
2066
1967
    def get_format_description(self):
2067
 
        self._ensure_real()
2068
 
        return 'Remote: ' + self._custom_format.get_format_description()
 
1968
        return 'Remote BZR Branch'
2069
1969
 
2070
1970
    def network_name(self):
2071
1971
        return self._network_name
2072
1972
 
2073
 
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2074
 
        return a_bzrdir.open_branch(name=name, 
2075
 
            ignore_fallbacks=ignore_fallbacks)
 
1973
    def open(self, a_bzrdir, ignore_fallbacks=False):
 
1974
        return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2076
1975
 
2077
 
    def _vfs_initialize(self, a_bzrdir, name):
 
1976
    def _vfs_initialize(self, a_bzrdir):
2078
1977
        # Initialisation when using a local bzrdir object, or a non-vfs init
2079
1978
        # method is not available on the server.
2080
1979
        # self._custom_format is always set - the start of initialize ensures
2081
1980
        # that.
2082
1981
        if isinstance(a_bzrdir, RemoteBzrDir):
2083
1982
            a_bzrdir._ensure_real()
2084
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2085
 
                name)
 
1983
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2086
1984
        else:
2087
1985
            # We assume the bzrdir is parameterised; it may not be.
2088
 
            result = self._custom_format.initialize(a_bzrdir, name)
 
1986
            result = self._custom_format.initialize(a_bzrdir)
2089
1987
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2090
1988
            not isinstance(result, RemoteBranch)):
2091
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2092
 
                                  name=name)
 
1989
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2093
1990
        return result
2094
1991
 
2095
 
    def initialize(self, a_bzrdir, name=None):
 
1992
    def initialize(self, a_bzrdir):
2096
1993
        # 1) get the network name to use.
2097
1994
        if self._custom_format:
2098
1995
            network_name = self._custom_format.network_name()
2104
2001
            network_name = reference_format.network_name()
2105
2002
        # Being asked to create on a non RemoteBzrDir:
2106
2003
        if not isinstance(a_bzrdir, RemoteBzrDir):
2107
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2004
            return self._vfs_initialize(a_bzrdir)
2108
2005
        medium = a_bzrdir._client._medium
2109
2006
        if medium._is_remote_before((1, 13)):
2110
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2007
            return self._vfs_initialize(a_bzrdir)
2111
2008
        # Creating on a remote bzr dir.
2112
2009
        # 2) try direct creation via RPC
2113
2010
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2114
 
        if name is not None:
2115
 
            # XXX JRV20100304: Support creating colocated branches
2116
 
            raise errors.NoColocatedBranchSupport(self)
2117
2011
        verb = 'BzrDir.create_branch'
2118
2012
        try:
2119
2013
            response = a_bzrdir._call(verb, path, network_name)
2120
2014
        except errors.UnknownSmartMethod:
2121
2015
            # Fallback - use vfs methods
2122
2016
            medium._remember_remote_is_before((1, 13))
2123
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2017
            return self._vfs_initialize(a_bzrdir)
2124
2018
        if response[0] != 'ok':
2125
2019
            raise errors.UnexpectedSmartServerResponse(response)
2126
2020
        # Turn the response into a RemoteRepository object.
2134
2028
                a_bzrdir._client)
2135
2029
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2136
2030
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2137
 
            format=format, setup_stacking=False, name=name)
 
2031
            format=format, setup_stacking=False)
2138
2032
        # XXX: We know this is a new branch, so it must have revno 0, revid
2139
2033
        # NULL_REVISION. Creating the branch locked would make this be unable
2140
2034
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2160
2054
        return self._custom_format.supports_set_append_revisions_only()
2161
2055
 
2162
2056
 
2163
 
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
 
2057
class RemoteBranch(branch.Branch, _RpcHelper):
2164
2058
    """Branch stored on a server accessed by HPSS RPC.
2165
2059
 
2166
2060
    At the moment most operations are mapped down to simple file operations.
2167
2061
    """
2168
2062
 
2169
2063
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2170
 
        _client=None, format=None, setup_stacking=True, name=None):
 
2064
        _client=None, format=None, setup_stacking=True):
2171
2065
        """Create a RemoteBranch instance.
2172
2066
 
2173
2067
        :param real_branch: An optional local implementation of the branch
2179
2073
        :param setup_stacking: If True make an RPC call to determine the
2180
2074
            stacked (or not) status of the branch. If False assume the branch
2181
2075
            is not stacked.
2182
 
        :param name: Colocated branch name
2183
2076
        """
2184
2077
        # We intentionally don't call the parent class's __init__, because it
2185
2078
        # will try to assign to self.tags, which is a property in this subclass.
2204
2097
            self._real_branch = None
2205
2098
        # Fill out expected attributes of branch for bzrlib API users.
2206
2099
        self._clear_cached_state()
2207
 
        # TODO: deprecate self.base in favor of user_url
2208
 
        self.base = self.bzrdir.user_url
2209
 
        self._name = name
 
2100
        self.base = self.bzrdir.root_transport.base
2210
2101
        self._control_files = None
2211
2102
        self._lock_mode = None
2212
2103
        self._lock_token = None
2277
2168
                    'to use vfs implementation')
2278
2169
            self.bzrdir._ensure_real()
2279
2170
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2280
 
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
 
2171
                ignore_fallbacks=self._real_ignore_fallbacks)
2281
2172
            if self.repository._real_repository is None:
2282
2173
                # Give the remote repository the matching real repo.
2283
2174
                real_repo = self._real_branch.repository
2387
2278
        medium = self._client._medium
2388
2279
        if medium._is_remote_before((1, 18)):
2389
2280
            self._vfs_set_tags_bytes(bytes)
2390
 
            return
2391
2281
        try:
2392
2282
            args = (
2393
2283
                self._remote_path(), self._lock_token, self._repo_lock_token)
2398
2288
            self._vfs_set_tags_bytes(bytes)
2399
2289
 
2400
2290
    def lock_read(self):
2401
 
        """Lock the branch for read operations.
2402
 
 
2403
 
        :return: A bzrlib.lock.LogicalLockResult.
2404
 
        """
2405
2291
        self.repository.lock_read()
2406
2292
        if not self._lock_mode:
2407
 
            self._note_lock('r')
2408
2293
            self._lock_mode = 'r'
2409
2294
            self._lock_count = 1
2410
2295
            if self._real_branch is not None:
2411
2296
                self._real_branch.lock_read()
2412
2297
        else:
2413
2298
            self._lock_count += 1
2414
 
        return lock.LogicalLockResult(self.unlock)
2415
2299
 
2416
2300
    def _remote_lock_write(self, token):
2417
2301
        if token is None:
2418
2302
            branch_token = repo_token = ''
2419
2303
        else:
2420
2304
            branch_token = token
2421
 
            repo_token = self.repository.lock_write().repository_token
 
2305
            repo_token = self.repository.lock_write()
2422
2306
            self.repository.unlock()
2423
2307
        err_context = {'token': token}
2424
 
        try:
2425
 
            response = self._call(
2426
 
                'Branch.lock_write', self._remote_path(), branch_token,
2427
 
                repo_token or '', **err_context)
2428
 
        except errors.LockContention, e:
2429
 
            # The LockContention from the server doesn't have any
2430
 
            # information about the lock_url. We re-raise LockContention
2431
 
            # with valid lock_url.
2432
 
            raise errors.LockContention('(remote lock)',
2433
 
                self.repository.base.split('.bzr/')[0])
 
2308
        response = self._call(
 
2309
            'Branch.lock_write', self._remote_path(), branch_token,
 
2310
            repo_token or '', **err_context)
2434
2311
        if response[0] != 'ok':
2435
2312
            raise errors.UnexpectedSmartServerResponse(response)
2436
2313
        ok, branch_token, repo_token = response
2438
2315
 
2439
2316
    def lock_write(self, token=None):
2440
2317
        if not self._lock_mode:
2441
 
            self._note_lock('w')
2442
2318
            # Lock the branch and repo in one remote call.
2443
2319
            remote_tokens = self._remote_lock_write(token)
2444
2320
            self._lock_token, self._repo_lock_token = remote_tokens
2457
2333
            self._lock_mode = 'w'
2458
2334
            self._lock_count = 1
2459
2335
        elif self._lock_mode == 'r':
2460
 
            raise errors.ReadOnlyError(self)
 
2336
            raise errors.ReadOnlyTransaction
2461
2337
        else:
2462
2338
            if token is not None:
2463
2339
                # A token was given to lock_write, and we're relocking, so
2468
2344
            self._lock_count += 1
2469
2345
            # Re-lock the repository too.
2470
2346
            self.repository.lock_write(self._repo_lock_token)
2471
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2347
        return self._lock_token or None
2472
2348
 
2473
2349
    def _unlock(self, branch_token, repo_token):
2474
2350
        err_context = {'token': str((branch_token, repo_token))}
2479
2355
            return
2480
2356
        raise errors.UnexpectedSmartServerResponse(response)
2481
2357
 
2482
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2483
2358
    def unlock(self):
2484
2359
        try:
2485
2360
            self._lock_count -= 1
2525
2400
            raise NotImplementedError(self.dont_leave_lock_in_place)
2526
2401
        self._leave_lock = False
2527
2402
 
2528
 
    @needs_read_lock
2529
2403
    def get_rev_id(self, revno, history=None):
2530
2404
        if revno == 0:
2531
2405
            return _mod_revision.NULL_REVISION
2797
2671
        medium = self._branch._client._medium
2798
2672
        if medium._is_remote_before((1, 14)):
2799
2673
            return self._vfs_set_option(value, name, section)
2800
 
        if isinstance(value, dict):
2801
 
            if medium._is_remote_before((2, 2)):
2802
 
                return self._vfs_set_option(value, name, section)
2803
 
            return self._set_config_option_dict(value, name, section)
2804
 
        else:
2805
 
            return self._set_config_option(value, name, section)
2806
 
 
2807
 
    def _set_config_option(self, value, name, section):
2808
2674
        try:
2809
2675
            path = self._branch._remote_path()
2810
2676
            response = self._branch._client.call('Branch.set_config_option',
2811
2677
                path, self._branch._lock_token, self._branch._repo_lock_token,
2812
2678
                value.encode('utf8'), name, section or '')
2813
2679
        except errors.UnknownSmartMethod:
2814
 
            medium = self._branch._client._medium
2815
2680
            medium._remember_remote_is_before((1, 14))
2816
2681
            return self._vfs_set_option(value, name, section)
2817
2682
        if response != ():
2818
2683
            raise errors.UnexpectedSmartServerResponse(response)
2819
2684
 
2820
 
    def _serialize_option_dict(self, option_dict):
2821
 
        utf8_dict = {}
2822
 
        for key, value in option_dict.items():
2823
 
            if isinstance(key, unicode):
2824
 
                key = key.encode('utf8')
2825
 
            if isinstance(value, unicode):
2826
 
                value = value.encode('utf8')
2827
 
            utf8_dict[key] = value
2828
 
        return bencode.bencode(utf8_dict)
2829
 
 
2830
 
    def _set_config_option_dict(self, value, name, section):
2831
 
        try:
2832
 
            path = self._branch._remote_path()
2833
 
            serialised_dict = self._serialize_option_dict(value)
2834
 
            response = self._branch._client.call(
2835
 
                'Branch.set_config_option_dict',
2836
 
                path, self._branch._lock_token, self._branch._repo_lock_token,
2837
 
                serialised_dict, name, section or '')
2838
 
        except errors.UnknownSmartMethod:
2839
 
            medium = self._branch._client._medium
2840
 
            medium._remember_remote_is_before((2, 2))
2841
 
            return self._vfs_set_option(value, name, section)
2842
 
        if response != ():
2843
 
            raise errors.UnexpectedSmartServerResponse(response)
2844
 
 
2845
2685
    def _real_object(self):
2846
2686
        self._branch._ensure_real()
2847
2687
        return self._branch._real_branch
2930
2770
                    'Missing key %r in context %r', key_err.args[0], context)
2931
2771
                raise err
2932
2772
 
2933
 
    if err.error_verb == 'IncompatibleRepositories':
2934
 
        raise errors.IncompatibleRepositories(err.error_args[0],
2935
 
            err.error_args[1], err.error_args[2])
2936
 
    elif err.error_verb == 'NoSuchRevision':
 
2773
    if err.error_verb == 'NoSuchRevision':
2937
2774
        raise NoSuchRevision(find('branch'), err.error_args[0])
2938
2775
    elif err.error_verb == 'nosuchrevision':
2939
2776
        raise NoSuchRevision(find('repository'), err.error_args[0])
2940
 
    elif err.error_verb == 'nobranch':
2941
 
        if len(err.error_args) >= 1:
2942
 
            extra = err.error_args[0]
2943
 
        else:
2944
 
            extra = None
2945
 
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2946
 
            detail=extra)
 
2777
    elif err.error_tuple == ('nobranch',):
 
2778
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
2947
2779
    elif err.error_verb == 'norepository':
2948
2780
        raise errors.NoRepositoryPresent(find('bzrdir'))
2949
2781
    elif err.error_verb == 'LockContention':