~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Martin Pool
  • Date: 2009-09-14 02:30:23 UTC
  • mto: This revision was merged to the branch mainline in revision 4693.
  • Revision ID: mbp@sourcefrog.net-20090914023023-ros0f3ndo04j3bww
Clearer docs about bzr help.  (Thanks to Naoki)

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 = \
214
172
        if len(branch_info) != 2:
215
173
            raise errors.UnexpectedSmartServerResponse(response)
216
174
        branch_ref, branch_name = branch_info
217
 
        format = controldir.network_format_registry.get(control_name)
 
175
        format = bzrdir.network_format_registry.get(control_name)
218
176
        if repo_name:
219
177
            format.repository_format = repository.network_format_registry.get(
220
178
                repo_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__
648
583
        return self._custom_format._serializer
649
584
 
650
585
 
651
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
652
 
    controldir.ControlComponent):
 
586
class RemoteRepository(_RpcHelper):
653
587
    """Repository accessed over rpc.
654
588
 
655
589
    For the moment most operations are performed using local transport-backed
698
632
        # Additional places to query for data.
699
633
        self._fallback_repositories = []
700
634
 
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
635
    def __str__(self):
713
636
        return "%s(%s)" % (self.__class__.__name__, self.base)
714
637
 
900
823
    def _has_same_fallbacks(self, other_repo):
901
824
        """Returns true if the repositories have the same fallbacks."""
902
825
        # XXX: copied from Repository; it should be unified into a base class
903
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
826
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
904
827
        my_fb = self._fallback_repositories
905
828
        other_fb = other_repo._fallback_repositories
906
829
        if len(my_fb) != len(other_fb):
922
845
        parents_provider = self._make_parents_provider(other_repository)
923
846
        return graph.Graph(parents_provider)
924
847
 
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
848
    def gather_stats(self, revid=None, committers=None):
935
849
        """See Repository.gather_stats()."""
936
850
        path = self.bzrdir._path_for_remote_call(self._client)
996
910
    def is_write_locked(self):
997
911
        return self._lock_mode == 'w'
998
912
 
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
913
    def lock_read(self):
1005
 
        """Lock the repository for read operations.
1006
 
 
1007
 
        :return: A bzrlib.lock.LogicalLockResult.
1008
 
        """
1009
914
        # wrong eventually - want a local lock cache context
1010
915
        if not self._lock_mode:
1011
 
            self._note_lock('r')
1012
916
            self._lock_mode = 'r'
1013
917
            self._lock_count = 1
1014
918
            self._unstacked_provider.enable_cache(cache_misses=True)
1018
922
                repo.lock_read()
1019
923
        else:
1020
924
            self._lock_count += 1
1021
 
        return lock.LogicalLockResult(self.unlock)
1022
925
 
1023
926
    def _remote_lock_write(self, token):
1024
927
        path = self.bzrdir._path_for_remote_call(self._client)
1035
938
 
1036
939
    def lock_write(self, token=None, _skip_rpc=False):
1037
940
        if not self._lock_mode:
1038
 
            self._note_lock('w')
1039
941
            if _skip_rpc:
1040
942
                if self._lock_token is not None:
1041
943
                    if token != self._lock_token:
1064
966
            raise errors.ReadOnlyError(self)
1065
967
        else:
1066
968
            self._lock_count += 1
1067
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
969
        return self._lock_token or None
1068
970
 
1069
971
    def leave_lock_in_place(self):
1070
972
        if not self._lock_token:
1144
1046
        else:
1145
1047
            raise errors.UnexpectedSmartServerResponse(response)
1146
1048
 
1147
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
1148
1049
    def unlock(self):
1149
1050
        if not self._lock_count:
1150
1051
            return lock.cant_unlock_not_held(self)
1250
1151
            # state, so always add a lock here. If a caller passes us a locked
1251
1152
            # repository, they are responsible for unlocking it later.
1252
1153
            repository.lock_read()
1253
 
        self._check_fallback_repository(repository)
1254
1154
        self._fallback_repositories.append(repository)
1255
1155
        # If self._real_repository was parameterised already (e.g. because a
1256
1156
        # _real_branch had its get_stacked_on_url method called), then the
1257
1157
        # repository to be added may already be in the _real_repositories list.
1258
1158
        if self._real_repository is not None:
1259
 
            fallback_locations = [repo.user_url for repo in
 
1159
            fallback_locations = [repo.bzrdir.root_transport.base for repo in
1260
1160
                self._real_repository._fallback_repositories]
1261
 
            if repository.user_url not in fallback_locations:
 
1161
            if repository.bzrdir.root_transport.base not in fallback_locations:
1262
1162
                self._real_repository.add_fallback_repository(repository)
1263
1163
 
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
1164
    def add_inventory(self, revid, inv, parents):
1275
1165
        self._ensure_real()
1276
1166
        return self._real_repository.add_inventory(revid, inv, parents)
1277
1167
 
1278
1168
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1279
 
            parents, basis_inv=None, propagate_caches=False):
 
1169
                               parents):
1280
1170
        self._ensure_real()
1281
1171
        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)
 
1172
            delta, new_revision_id, parents)
1284
1173
 
1285
1174
    def add_revision(self, rev_id, rev, inv=None, config=None):
1286
1175
        self._ensure_real()
1316
1205
        return self._real_repository.make_working_trees()
1317
1206
 
1318
1207
    def refresh_data(self):
1319
 
        """Re-read any data needed to synchronise with disk.
 
1208
        """Re-read any data needed to to synchronise with disk.
1320
1209
 
1321
1210
        This method is intended to be called after another repository instance
1322
1211
        (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.
 
1212
        repository. It may not be called during a write group, but may be
 
1213
        called at any other time.
1328
1214
        """
 
1215
        if self.is_in_write_group():
 
1216
            raise errors.InternalBzrError(
 
1217
                "May not refresh_data while in a write group.")
1329
1218
        if self._real_repository is not None:
1330
1219
            self._real_repository.refresh_data()
1331
1220
 
1545
1434
        return self._real_repository.get_signature_text(revision_id)
1546
1435
 
1547
1436
    @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)
 
1437
    def get_inventory_xml(self, revision_id):
 
1438
        self._ensure_real()
 
1439
        return self._real_repository.get_inventory_xml(revision_id)
 
1440
 
 
1441
    def deserialise_inventory(self, revision_id, xml):
 
1442
        self._ensure_real()
 
1443
        return self._real_repository.deserialise_inventory(revision_id, xml)
1551
1444
 
1552
1445
    def reconcile(self, other=None, thorough=False):
1553
1446
        self._ensure_real()
1629
1522
        return self._real_repository.inventories
1630
1523
 
1631
1524
    @needs_write_lock
1632
 
    def pack(self, hint=None, clean_obsolete_packs=False):
 
1525
    def pack(self, hint=None):
1633
1526
        """Compress the data within the repository.
1634
1527
 
1635
1528
        This is not currently implemented within the smart server.
1636
1529
        """
1637
1530
        self._ensure_real()
1638
 
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
 
1531
        return self._real_repository.pack(hint=hint)
1639
1532
 
1640
1533
    @property
1641
1534
    def revisions(self):
1846
1739
            # The stream included an inventory-delta record, but the remote
1847
1740
            # side isn't new enough to support them.  So we need to send the
1848
1741
            # rest of the stream via VFS.
1849
 
            self.target_repo.refresh_data()
1850
1742
            return self._resume_stream_with_vfs(response, src_format)
1851
1743
        if response[0][0] == 'missing-basis':
1852
1744
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1981
1873
        if response_tuple[0] != 'ok':
1982
1874
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1983
1875
        byte_stream = response_handler.read_streamed_body()
1984
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
1985
 
            self._record_counter)
 
1876
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
1986
1877
        if src_format.network_name() != repo._format.network_name():
1987
1878
            raise AssertionError(
1988
1879
                "Mismatched RemoteRepository and stream src %r, %r" % (
1995
1886
        :param search: The overall search to satisfy with streams.
1996
1887
        :param sources: A list of Repository objects to query.
1997
1888
        """
1998
 
        self.from_serialiser = self.from_repository._format._serializer
 
1889
        self.serialiser = self.to_format._serializer
1999
1890
        self.seen_revs = set()
2000
1891
        self.referenced_revs = set()
2001
1892
        # If there are heads in the search, or the key count is > 0, we are not
2018
1909
    def missing_parents_rev_handler(self, substream):
2019
1910
        for content in substream:
2020
1911
            revision_bytes = content.get_bytes_as('fulltext')
2021
 
            revision = self.from_serialiser.read_revision_from_string(
2022
 
                revision_bytes)
 
1912
            revision = self.serialiser.read_revision_from_string(revision_bytes)
2023
1913
            self.seen_revs.add(content.key[-1])
2024
1914
            self.referenced_revs.update(revision.parent_ids)
2025
1915
            yield content
2064
1954
                self._network_name)
2065
1955
 
2066
1956
    def get_format_description(self):
2067
 
        self._ensure_real()
2068
 
        return 'Remote: ' + self._custom_format.get_format_description()
 
1957
        return 'Remote BZR Branch'
2069
1958
 
2070
1959
    def network_name(self):
2071
1960
        return self._network_name
2072
1961
 
2073
 
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2074
 
        return a_bzrdir.open_branch(name=name, 
2075
 
            ignore_fallbacks=ignore_fallbacks)
 
1962
    def open(self, a_bzrdir, ignore_fallbacks=False):
 
1963
        return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2076
1964
 
2077
 
    def _vfs_initialize(self, a_bzrdir, name):
 
1965
    def _vfs_initialize(self, a_bzrdir):
2078
1966
        # Initialisation when using a local bzrdir object, or a non-vfs init
2079
1967
        # method is not available on the server.
2080
1968
        # self._custom_format is always set - the start of initialize ensures
2081
1969
        # that.
2082
1970
        if isinstance(a_bzrdir, RemoteBzrDir):
2083
1971
            a_bzrdir._ensure_real()
2084
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2085
 
                name)
 
1972
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2086
1973
        else:
2087
1974
            # We assume the bzrdir is parameterised; it may not be.
2088
 
            result = self._custom_format.initialize(a_bzrdir, name)
 
1975
            result = self._custom_format.initialize(a_bzrdir)
2089
1976
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2090
1977
            not isinstance(result, RemoteBranch)):
2091
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2092
 
                                  name=name)
 
1978
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2093
1979
        return result
2094
1980
 
2095
 
    def initialize(self, a_bzrdir, name=None):
 
1981
    def initialize(self, a_bzrdir):
2096
1982
        # 1) get the network name to use.
2097
1983
        if self._custom_format:
2098
1984
            network_name = self._custom_format.network_name()
2104
1990
            network_name = reference_format.network_name()
2105
1991
        # Being asked to create on a non RemoteBzrDir:
2106
1992
        if not isinstance(a_bzrdir, RemoteBzrDir):
2107
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
1993
            return self._vfs_initialize(a_bzrdir)
2108
1994
        medium = a_bzrdir._client._medium
2109
1995
        if medium._is_remote_before((1, 13)):
2110
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
1996
            return self._vfs_initialize(a_bzrdir)
2111
1997
        # Creating on a remote bzr dir.
2112
1998
        # 2) try direct creation via RPC
2113
1999
        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
2000
        verb = 'BzrDir.create_branch'
2118
2001
        try:
2119
2002
            response = a_bzrdir._call(verb, path, network_name)
2120
2003
        except errors.UnknownSmartMethod:
2121
2004
            # Fallback - use vfs methods
2122
2005
            medium._remember_remote_is_before((1, 13))
2123
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2006
            return self._vfs_initialize(a_bzrdir)
2124
2007
        if response[0] != 'ok':
2125
2008
            raise errors.UnexpectedSmartServerResponse(response)
2126
2009
        # Turn the response into a RemoteRepository object.
2134
2017
                a_bzrdir._client)
2135
2018
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2136
2019
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2137
 
            format=format, setup_stacking=False, name=name)
 
2020
            format=format, setup_stacking=False)
2138
2021
        # XXX: We know this is a new branch, so it must have revno 0, revid
2139
2022
        # NULL_REVISION. Creating the branch locked would make this be unable
2140
2023
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2160
2043
        return self._custom_format.supports_set_append_revisions_only()
2161
2044
 
2162
2045
 
2163
 
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
 
2046
class RemoteBranch(branch.Branch, _RpcHelper):
2164
2047
    """Branch stored on a server accessed by HPSS RPC.
2165
2048
 
2166
2049
    At the moment most operations are mapped down to simple file operations.
2167
2050
    """
2168
2051
 
2169
2052
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2170
 
        _client=None, format=None, setup_stacking=True, name=None):
 
2053
        _client=None, format=None, setup_stacking=True):
2171
2054
        """Create a RemoteBranch instance.
2172
2055
 
2173
2056
        :param real_branch: An optional local implementation of the branch
2179
2062
        :param setup_stacking: If True make an RPC call to determine the
2180
2063
            stacked (or not) status of the branch. If False assume the branch
2181
2064
            is not stacked.
2182
 
        :param name: Colocated branch name
2183
2065
        """
2184
2066
        # We intentionally don't call the parent class's __init__, because it
2185
2067
        # will try to assign to self.tags, which is a property in this subclass.
2204
2086
            self._real_branch = None
2205
2087
        # Fill out expected attributes of branch for bzrlib API users.
2206
2088
        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
 
2089
        self.base = self.bzrdir.root_transport.base
2210
2090
        self._control_files = None
2211
2091
        self._lock_mode = None
2212
2092
        self._lock_token = None
2277
2157
                    'to use vfs implementation')
2278
2158
            self.bzrdir._ensure_real()
2279
2159
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2280
 
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
 
2160
                ignore_fallbacks=self._real_ignore_fallbacks)
2281
2161
            if self.repository._real_repository is None:
2282
2162
                # Give the remote repository the matching real repo.
2283
2163
                real_repo = self._real_branch.repository
2387
2267
        medium = self._client._medium
2388
2268
        if medium._is_remote_before((1, 18)):
2389
2269
            self._vfs_set_tags_bytes(bytes)
2390
 
            return
2391
2270
        try:
2392
2271
            args = (
2393
2272
                self._remote_path(), self._lock_token, self._repo_lock_token)
2398
2277
            self._vfs_set_tags_bytes(bytes)
2399
2278
 
2400
2279
    def lock_read(self):
2401
 
        """Lock the branch for read operations.
2402
 
 
2403
 
        :return: A bzrlib.lock.LogicalLockResult.
2404
 
        """
2405
2280
        self.repository.lock_read()
2406
2281
        if not self._lock_mode:
2407
 
            self._note_lock('r')
2408
2282
            self._lock_mode = 'r'
2409
2283
            self._lock_count = 1
2410
2284
            if self._real_branch is not None:
2411
2285
                self._real_branch.lock_read()
2412
2286
        else:
2413
2287
            self._lock_count += 1
2414
 
        return lock.LogicalLockResult(self.unlock)
2415
2288
 
2416
2289
    def _remote_lock_write(self, token):
2417
2290
        if token is None:
2418
2291
            branch_token = repo_token = ''
2419
2292
        else:
2420
2293
            branch_token = token
2421
 
            repo_token = self.repository.lock_write().repository_token
 
2294
            repo_token = self.repository.lock_write()
2422
2295
            self.repository.unlock()
2423
2296
        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])
 
2297
        response = self._call(
 
2298
            'Branch.lock_write', self._remote_path(), branch_token,
 
2299
            repo_token or '', **err_context)
2434
2300
        if response[0] != 'ok':
2435
2301
            raise errors.UnexpectedSmartServerResponse(response)
2436
2302
        ok, branch_token, repo_token = response
2438
2304
 
2439
2305
    def lock_write(self, token=None):
2440
2306
        if not self._lock_mode:
2441
 
            self._note_lock('w')
2442
2307
            # Lock the branch and repo in one remote call.
2443
2308
            remote_tokens = self._remote_lock_write(token)
2444
2309
            self._lock_token, self._repo_lock_token = remote_tokens
2457
2322
            self._lock_mode = 'w'
2458
2323
            self._lock_count = 1
2459
2324
        elif self._lock_mode == 'r':
2460
 
            raise errors.ReadOnlyError(self)
 
2325
            raise errors.ReadOnlyTransaction
2461
2326
        else:
2462
2327
            if token is not None:
2463
2328
                # A token was given to lock_write, and we're relocking, so
2468
2333
            self._lock_count += 1
2469
2334
            # Re-lock the repository too.
2470
2335
            self.repository.lock_write(self._repo_lock_token)
2471
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2336
        return self._lock_token or None
2472
2337
 
2473
2338
    def _unlock(self, branch_token, repo_token):
2474
2339
        err_context = {'token': str((branch_token, repo_token))}
2479
2344
            return
2480
2345
        raise errors.UnexpectedSmartServerResponse(response)
2481
2346
 
2482
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2483
2347
    def unlock(self):
2484
2348
        try:
2485
2349
            self._lock_count -= 1
2525
2389
            raise NotImplementedError(self.dont_leave_lock_in_place)
2526
2390
        self._leave_lock = False
2527
2391
 
2528
 
    @needs_read_lock
2529
2392
    def get_rev_id(self, revno, history=None):
2530
2393
        if revno == 0:
2531
2394
            return _mod_revision.NULL_REVISION
2797
2660
        medium = self._branch._client._medium
2798
2661
        if medium._is_remote_before((1, 14)):
2799
2662
            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
2663
        try:
2809
2664
            path = self._branch._remote_path()
2810
2665
            response = self._branch._client.call('Branch.set_config_option',
2811
2666
                path, self._branch._lock_token, self._branch._repo_lock_token,
2812
2667
                value.encode('utf8'), name, section or '')
2813
2668
        except errors.UnknownSmartMethod:
2814
 
            medium = self._branch._client._medium
2815
2669
            medium._remember_remote_is_before((1, 14))
2816
2670
            return self._vfs_set_option(value, name, section)
2817
2671
        if response != ():
2818
2672
            raise errors.UnexpectedSmartServerResponse(response)
2819
2673
 
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
2674
    def _real_object(self):
2846
2675
        self._branch._ensure_real()
2847
2676
        return self._branch._real_branch
2937
2766
        raise NoSuchRevision(find('branch'), err.error_args[0])
2938
2767
    elif err.error_verb == 'nosuchrevision':
2939
2768
        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)
 
2769
    elif err.error_tuple == ('nobranch',):
 
2770
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
2947
2771
    elif err.error_verb == 'norepository':
2948
2772
        raise errors.NoRepositoryPresent(find('bzrdir'))
2949
2773
    elif err.error_verb == 'LockContention':