~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: John Arbash Meinel
  • Date: 2010-08-23 18:59:22 UTC
  • mto: This revision was merged to the branch mainline in revision 5390.
  • Revision ID: john@arbash-meinel.com-20100823185922-ovs1s9huk95wngk8
Update What's New and NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2006-2010 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
27
27
    lock,
28
28
    lockdir,
29
29
    repository,
 
30
    repository as _mod_repository,
30
31
    revision,
31
32
    revision as _mod_revision,
 
33
    static_tuple,
32
34
    symbol_versioning,
33
35
)
34
 
from bzrlib.branch import BranchReferenceFormat
 
36
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
35
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
36
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
37
39
from bzrlib.errors import (
38
40
    NoSuchRevision,
39
41
    SmartProtocolError,
41
43
from bzrlib.lockable_files import LockableFiles
42
44
from bzrlib.smart import client, vfs, repository as smart_repo
43
45
from bzrlib.revision import ensure_null, NULL_REVISION
 
46
from bzrlib.repository import RepositoryWriteLockResult
44
47
from bzrlib.trace import mutter, note, warning
45
48
 
46
49
 
89
92
class RemoteBzrDir(BzrDir, _RpcHelper):
90
93
    """Control directory on a remote server, accessed via bzr:// or similar."""
91
94
 
92
 
    def __init__(self, transport, format, _client=None):
 
95
    def __init__(self, transport, format, _client=None, _force_probe=False):
93
96
        """Construct a RemoteBzrDir.
94
97
 
95
98
        :param _client: Private parameter for testing. Disables probing and the
99
102
        # this object holds a delegated bzrdir that uses file-level operations
100
103
        # to talk to the other side
101
104
        self._real_bzrdir = None
 
105
        self._has_working_tree = None
102
106
        # 1-shot cache for the call pattern 'create_branch; open_branch' - see
103
107
        # create_branch for details.
104
108
        self._next_open_branch_result = None
108
112
            self._client = client._SmartClient(medium)
109
113
        else:
110
114
            self._client = _client
111
 
            return
112
 
 
 
115
            if not _force_probe:
 
116
                return
 
117
 
 
118
        self._probe_bzrdir()
 
119
 
 
120
    def __repr__(self):
 
121
        return '%s(%r)' % (self.__class__.__name__, self._client)
 
122
 
 
123
    def _probe_bzrdir(self):
 
124
        medium = self._client._medium
113
125
        path = self._path_for_remote_call(self._client)
 
126
        if medium._is_remote_before((2, 1)):
 
127
            self._rpc_open(path)
 
128
            return
 
129
        try:
 
130
            self._rpc_open_2_1(path)
 
131
            return
 
132
        except errors.UnknownSmartMethod:
 
133
            medium._remember_remote_is_before((2, 1))
 
134
            self._rpc_open(path)
 
135
 
 
136
    def _rpc_open_2_1(self, path):
 
137
        response = self._call('BzrDir.open_2.1', path)
 
138
        if response == ('no',):
 
139
            raise errors.NotBranchError(path=self.root_transport.base)
 
140
        elif response[0] == 'yes':
 
141
            if response[1] == 'yes':
 
142
                self._has_working_tree = True
 
143
            elif response[1] == 'no':
 
144
                self._has_working_tree = False
 
145
            else:
 
146
                raise errors.UnexpectedSmartServerResponse(response)
 
147
        else:
 
148
            raise errors.UnexpectedSmartServerResponse(response)
 
149
 
 
150
    def _rpc_open(self, path):
114
151
        response = self._call('BzrDir.open', path)
115
152
        if response not in [('yes',), ('no',)]:
116
153
            raise errors.UnexpectedSmartServerResponse(response)
117
154
        if response == ('no',):
118
 
            raise errors.NotBranchError(path=transport.base)
 
155
            raise errors.NotBranchError(path=self.root_transport.base)
119
156
 
120
157
    def _ensure_real(self):
121
158
        """Ensure that there is a _real_bzrdir set.
123
160
        Used before calls to self._real_bzrdir.
124
161
        """
125
162
        if not self._real_bzrdir:
 
163
            if 'hpssvfs' in debug.debug_flags:
 
164
                import traceback
 
165
                warning('VFS BzrDir access triggered\n%s',
 
166
                    ''.join(traceback.format_stack()))
126
167
            self._real_bzrdir = BzrDir.open_from_transport(
127
168
                self.root_transport, _server_formats=False)
128
169
            self._format._network_name = \
204
245
        self._ensure_real()
205
246
        self._real_bzrdir.destroy_repository()
206
247
 
207
 
    def create_branch(self):
 
248
    def create_branch(self, name=None):
208
249
        # as per meta1 formats - just delegate to the format object which may
209
250
        # be parameterised.
210
 
        real_branch = self._format.get_branch_format().initialize(self)
 
251
        real_branch = self._format.get_branch_format().initialize(self,
 
252
            name=name)
211
253
        if not isinstance(real_branch, RemoteBranch):
212
 
            result = RemoteBranch(self, self.find_repository(), real_branch)
 
254
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
255
                                  name=name)
213
256
        else:
214
257
            result = real_branch
215
258
        # BzrDir.clone_on_transport() uses the result of create_branch but does
221
264
        self._next_open_branch_result = result
222
265
        return result
223
266
 
224
 
    def destroy_branch(self):
 
267
    def destroy_branch(self, name=None):
225
268
        """See BzrDir.destroy_branch"""
226
269
        self._ensure_real()
227
 
        self._real_bzrdir.destroy_branch()
 
270
        self._real_bzrdir.destroy_branch(name=name)
228
271
        self._next_open_branch_result = None
229
272
 
230
273
    def create_workingtree(self, revision_id=None, from_branch=None):
231
274
        raise errors.NotLocalUrl(self.transport.base)
232
275
 
233
 
    def find_branch_format(self):
 
276
    def find_branch_format(self, name=None):
234
277
        """Find the branch 'format' for this bzrdir.
235
278
 
236
279
        This might be a synthetic object for e.g. RemoteBranch and SVN.
237
280
        """
238
 
        b = self.open_branch()
 
281
        b = self.open_branch(name=name)
239
282
        return b._format
240
283
 
241
 
    def get_branch_reference(self):
 
284
    def get_branch_reference(self, name=None):
242
285
        """See BzrDir.get_branch_reference()."""
 
286
        if name is not None:
 
287
            # XXX JRV20100304: Support opening colocated branches
 
288
            raise errors.NoColocatedBranchSupport(self)
243
289
        response = self._get_branch_reference()
244
290
        if response[0] == 'ref':
245
291
            return response[1]
249
295
    def _get_branch_reference(self):
250
296
        path = self._path_for_remote_call(self._client)
251
297
        medium = self._client._medium
252
 
        if not medium._is_remote_before((1, 13)):
 
298
        candidate_calls = [
 
299
            ('BzrDir.open_branchV3', (2, 1)),
 
300
            ('BzrDir.open_branchV2', (1, 13)),
 
301
            ('BzrDir.open_branch', None),
 
302
            ]
 
303
        for verb, required_version in candidate_calls:
 
304
            if required_version and medium._is_remote_before(required_version):
 
305
                continue
253
306
            try:
254
 
                response = self._call('BzrDir.open_branchV2', path)
255
 
                if response[0] not in ('ref', 'branch'):
256
 
                    raise errors.UnexpectedSmartServerResponse(response)
257
 
                return response
 
307
                response = self._call(verb, path)
258
308
            except errors.UnknownSmartMethod:
259
 
                medium._remember_remote_is_before((1, 13))
260
 
        response = self._call('BzrDir.open_branch', path)
261
 
        if response[0] != 'ok':
 
309
                if required_version is None:
 
310
                    raise
 
311
                medium._remember_remote_is_before(required_version)
 
312
            else:
 
313
                break
 
314
        if verb == 'BzrDir.open_branch':
 
315
            if response[0] != 'ok':
 
316
                raise errors.UnexpectedSmartServerResponse(response)
 
317
            if response[1] != '':
 
318
                return ('ref', response[1])
 
319
            else:
 
320
                return ('branch', '')
 
321
        if response[0] not in ('ref', 'branch'):
262
322
            raise errors.UnexpectedSmartServerResponse(response)
263
 
        if response[1] != '':
264
 
            return ('ref', response[1])
265
 
        else:
266
 
            return ('branch', '')
 
323
        return response
267
324
 
268
 
    def _get_tree_branch(self):
 
325
    def _get_tree_branch(self, name=None):
269
326
        """See BzrDir._get_tree_branch()."""
270
 
        return None, self.open_branch()
 
327
        return None, self.open_branch(name=name)
271
328
 
272
 
    def open_branch(self, _unsupported=False, ignore_fallbacks=False):
273
 
        if _unsupported:
 
329
    def open_branch(self, name=None, unsupported=False,
 
330
                    ignore_fallbacks=False):
 
331
        if unsupported:
274
332
            raise NotImplementedError('unsupported flag support not implemented yet.')
275
333
        if self._next_open_branch_result is not None:
276
334
            # See create_branch for details.
281
339
        if response[0] == 'ref':
282
340
            # a branch reference, use the existing BranchReference logic.
283
341
            format = BranchReferenceFormat()
284
 
            return format.open(self, _found=True, location=response[1],
285
 
                ignore_fallbacks=ignore_fallbacks)
 
342
            return format.open(self, name=name, _found=True,
 
343
                location=response[1], ignore_fallbacks=ignore_fallbacks)
286
344
        branch_format_name = response[1]
287
345
        if not branch_format_name:
288
346
            branch_format_name = None
289
347
        format = RemoteBranchFormat(network_name=branch_format_name)
290
348
        return RemoteBranch(self, self.find_repository(), format=format,
291
 
            setup_stacking=not ignore_fallbacks)
 
349
            setup_stacking=not ignore_fallbacks, name=name)
292
350
 
293
351
    def _open_repo_v1(self, path):
294
352
        verb = 'BzrDir.find_repository'
355
413
        else:
356
414
            raise errors.NoRepositoryPresent(self)
357
415
 
 
416
    def has_workingtree(self):
 
417
        if self._has_working_tree is None:
 
418
            self._ensure_real()
 
419
            self._has_working_tree = self._real_bzrdir.has_workingtree()
 
420
        return self._has_working_tree
 
421
 
358
422
    def open_workingtree(self, recommend_upgrade=True):
359
 
        self._ensure_real()
360
 
        if self._real_bzrdir.has_workingtree():
 
423
        if self.has_workingtree():
361
424
            raise errors.NotLocalUrl(self.root_transport)
362
425
        else:
363
426
            raise errors.NoWorkingTree(self.root_transport.base)
366
429
        """Return the path to be used for this bzrdir in a remote call."""
367
430
        return client.remote_path_from_transport(self.root_transport)
368
431
 
369
 
    def get_branch_transport(self, branch_format):
 
432
    def get_branch_transport(self, branch_format, name=None):
370
433
        self._ensure_real()
371
 
        return self._real_bzrdir.get_branch_transport(branch_format)
 
434
        return self._real_bzrdir.get_branch_transport(branch_format, name=name)
372
435
 
373
436
    def get_repository_transport(self, repository_format):
374
437
        self._ensure_real()
431
494
        self._supports_tree_reference = None
432
495
        self._rich_root_data = None
433
496
 
 
497
    def __repr__(self):
 
498
        return "%s(_network_name=%r)" % (self.__class__.__name__,
 
499
            self._network_name)
 
500
 
434
501
    @property
435
502
    def fast_deltas(self):
436
503
        self._ensure_real()
557
624
        return self._custom_format._fetch_reconcile
558
625
 
559
626
    def get_format_description(self):
560
 
        return 'bzr remote repository'
 
627
        self._ensure_real()
 
628
        return 'Remote: ' + self._custom_format.get_format_description()
561
629
 
562
630
    def __eq__(self, other):
563
631
        return self.__class__ is other.__class__
564
632
 
565
 
    def check_conversion_target(self, target_format):
566
 
        if self.rich_root_data and not target_format.rich_root_data:
567
 
            raise errors.BadConversionTarget(
568
 
                'Does not support rich root data.', target_format)
569
 
        if (self.supports_tree_reference and
570
 
            not getattr(target_format, 'supports_tree_reference', False)):
571
 
            raise errors.BadConversionTarget(
572
 
                'Does not support nested trees', target_format)
573
 
 
574
633
    def network_name(self):
575
634
        if self._network_name:
576
635
            return self._network_name
588
647
        return self._custom_format._serializer
589
648
 
590
649
 
591
 
class RemoteRepository(_RpcHelper):
 
650
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
 
651
    bzrdir.ControlComponent):
592
652
    """Repository accessed over rpc.
593
653
 
594
654
    For the moment most operations are performed using local transport-backed
637
697
        # Additional places to query for data.
638
698
        self._fallback_repositories = []
639
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
        
640
711
    def __str__(self):
641
712
        return "%s(%s)" % (self.__class__.__name__, self.base)
642
713
 
828
899
    def _has_same_fallbacks(self, other_repo):
829
900
        """Returns true if the repositories have the same fallbacks."""
830
901
        # XXX: copied from Repository; it should be unified into a base class
831
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
 
902
        # <https://bugs.launchpad.net/bzr/+bug/401622>
832
903
        my_fb = self._fallback_repositories
833
904
        other_fb = other_repo._fallback_repositories
834
905
        if len(my_fb) != len(other_fb):
850
921
        parents_provider = self._make_parents_provider(other_repository)
851
922
        return graph.Graph(parents_provider)
852
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
 
853
933
    def gather_stats(self, revid=None, committers=None):
854
934
        """See Repository.gather_stats()."""
855
935
        path = self.bzrdir._path_for_remote_call(self._client)
915
995
    def is_write_locked(self):
916
996
        return self._lock_mode == 'w'
917
997
 
 
998
    def _warn_if_deprecated(self, branch=None):
 
999
        # If we have a real repository, the check will be done there, if we
 
1000
        # don't the check will be done remotely.
 
1001
        pass
 
1002
 
918
1003
    def lock_read(self):
 
1004
        """Lock the repository for read operations.
 
1005
 
 
1006
        :return: A bzrlib.lock.LogicalLockResult.
 
1007
        """
919
1008
        # wrong eventually - want a local lock cache context
920
1009
        if not self._lock_mode:
 
1010
            self._note_lock('r')
921
1011
            self._lock_mode = 'r'
922
1012
            self._lock_count = 1
923
1013
            self._unstacked_provider.enable_cache(cache_misses=True)
927
1017
                repo.lock_read()
928
1018
        else:
929
1019
            self._lock_count += 1
 
1020
        return lock.LogicalLockResult(self.unlock)
930
1021
 
931
1022
    def _remote_lock_write(self, token):
932
1023
        path = self.bzrdir._path_for_remote_call(self._client)
943
1034
 
944
1035
    def lock_write(self, token=None, _skip_rpc=False):
945
1036
        if not self._lock_mode:
 
1037
            self._note_lock('w')
946
1038
            if _skip_rpc:
947
1039
                if self._lock_token is not None:
948
1040
                    if token != self._lock_token:
971
1063
            raise errors.ReadOnlyError(self)
972
1064
        else:
973
1065
            self._lock_count += 1
974
 
        return self._lock_token or None
 
1066
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
975
1067
 
976
1068
    def leave_lock_in_place(self):
977
1069
        if not self._lock_token:
1051
1143
        else:
1052
1144
            raise errors.UnexpectedSmartServerResponse(response)
1053
1145
 
 
1146
    @only_raises(errors.LockNotHeld, errors.LockBroken)
1054
1147
    def unlock(self):
1055
1148
        if not self._lock_count:
1056
1149
            return lock.cant_unlock_not_held(self)
1156
1249
            # state, so always add a lock here. If a caller passes us a locked
1157
1250
            # repository, they are responsible for unlocking it later.
1158
1251
            repository.lock_read()
 
1252
        self._check_fallback_repository(repository)
1159
1253
        self._fallback_repositories.append(repository)
1160
1254
        # If self._real_repository was parameterised already (e.g. because a
1161
1255
        # _real_branch had its get_stacked_on_url method called), then the
1162
1256
        # repository to be added may already be in the _real_repositories list.
1163
1257
        if self._real_repository is not None:
1164
 
            fallback_locations = [repo.bzrdir.root_transport.base for repo in
 
1258
            fallback_locations = [repo.user_url for repo in
1165
1259
                self._real_repository._fallback_repositories]
1166
 
            if repository.bzrdir.root_transport.base not in fallback_locations:
 
1260
            if repository.user_url not in fallback_locations:
1167
1261
                self._real_repository.add_fallback_repository(repository)
1168
1262
 
 
1263
    def _check_fallback_repository(self, repository):
 
1264
        """Check that this repository can fallback to repository safely.
 
1265
 
 
1266
        Raise an error if not.
 
1267
 
 
1268
        :param repository: A repository to fallback to.
 
1269
        """
 
1270
        return _mod_repository.InterRepository._assert_same_model(
 
1271
            self, repository)
 
1272
 
1169
1273
    def add_inventory(self, revid, inv, parents):
1170
1274
        self._ensure_real()
1171
1275
        return self._real_repository.add_inventory(revid, inv, parents)
1172
1276
 
1173
1277
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1174
 
                               parents):
 
1278
            parents, basis_inv=None, propagate_caches=False):
1175
1279
        self._ensure_real()
1176
1280
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
1177
 
            delta, new_revision_id, parents)
 
1281
            delta, new_revision_id, parents, basis_inv=basis_inv,
 
1282
            propagate_caches=propagate_caches)
1178
1283
 
1179
1284
    def add_revision(self, rev_id, rev, inv=None, config=None):
1180
1285
        self._ensure_real()
1210
1315
        return self._real_repository.make_working_trees()
1211
1316
 
1212
1317
    def refresh_data(self):
1213
 
        """Re-read any data needed to to synchronise with disk.
 
1318
        """Re-read any data needed to synchronise with disk.
1214
1319
 
1215
1320
        This method is intended to be called after another repository instance
1216
1321
        (such as one used by a smart server) has inserted data into the
1217
 
        repository. It may not be called during a write group, but may be
1218
 
        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.
1219
1327
        """
1220
 
        if self.is_in_write_group():
1221
 
            raise errors.InternalBzrError(
1222
 
                "May not refresh_data while in a write group.")
1223
1328
        if self._real_repository is not None:
1224
1329
            self._real_repository.refresh_data()
1225
1330
 
1439
1544
        return self._real_repository.get_signature_text(revision_id)
1440
1545
 
1441
1546
    @needs_read_lock
1442
 
    def get_inventory_xml(self, revision_id):
1443
 
        self._ensure_real()
1444
 
        return self._real_repository.get_inventory_xml(revision_id)
1445
 
 
1446
 
    def deserialise_inventory(self, revision_id, xml):
1447
 
        self._ensure_real()
1448
 
        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)
1449
1550
 
1450
1551
    def reconcile(self, other=None, thorough=False):
1451
1552
        self._ensure_real()
1527
1628
        return self._real_repository.inventories
1528
1629
 
1529
1630
    @needs_write_lock
1530
 
    def pack(self, hint=None):
 
1631
    def pack(self, hint=None, clean_obsolete_packs=False):
1531
1632
        """Compress the data within the repository.
1532
1633
 
1533
1634
        This is not currently implemented within the smart server.
1534
1635
        """
1535
1636
        self._ensure_real()
1536
 
        return self._real_repository.pack(hint=hint)
 
1637
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
1537
1638
 
1538
1639
    @property
1539
1640
    def revisions(self):
1744
1845
            # The stream included an inventory-delta record, but the remote
1745
1846
            # side isn't new enough to support them.  So we need to send the
1746
1847
            # rest of the stream via VFS.
 
1848
            self.target_repo.refresh_data()
1747
1849
            return self._resume_stream_with_vfs(response, src_format)
1748
1850
        if response[0][0] == 'missing-basis':
1749
1851
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1878
1980
        if response_tuple[0] != 'ok':
1879
1981
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1880
1982
        byte_stream = response_handler.read_streamed_body()
1881
 
        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)
1882
1985
        if src_format.network_name() != repo._format.network_name():
1883
1986
            raise AssertionError(
1884
1987
                "Mismatched RemoteRepository and stream src %r, %r" % (
1891
1994
        :param search: The overall search to satisfy with streams.
1892
1995
        :param sources: A list of Repository objects to query.
1893
1996
        """
1894
 
        self.serialiser = self.to_format._serializer
 
1997
        self.from_serialiser = self.from_repository._format._serializer
1895
1998
        self.seen_revs = set()
1896
1999
        self.referenced_revs = set()
1897
2000
        # If there are heads in the search, or the key count is > 0, we are not
1914
2017
    def missing_parents_rev_handler(self, substream):
1915
2018
        for content in substream:
1916
2019
            revision_bytes = content.get_bytes_as('fulltext')
1917
 
            revision = self.serialiser.read_revision_from_string(revision_bytes)
 
2020
            revision = self.from_serialiser.read_revision_from_string(
 
2021
                revision_bytes)
1918
2022
            self.seen_revs.add(content.key[-1])
1919
2023
            self.referenced_revs.update(revision.parent_ids)
1920
2024
            yield content
1959
2063
                self._network_name)
1960
2064
 
1961
2065
    def get_format_description(self):
1962
 
        return 'Remote BZR Branch'
 
2066
        self._ensure_real()
 
2067
        return 'Remote: ' + self._custom_format.get_format_description()
1963
2068
 
1964
2069
    def network_name(self):
1965
2070
        return self._network_name
1966
2071
 
1967
 
    def open(self, a_bzrdir, ignore_fallbacks=False):
1968
 
        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)
1969
2075
 
1970
 
    def _vfs_initialize(self, a_bzrdir):
 
2076
    def _vfs_initialize(self, a_bzrdir, name):
1971
2077
        # Initialisation when using a local bzrdir object, or a non-vfs init
1972
2078
        # method is not available on the server.
1973
2079
        # self._custom_format is always set - the start of initialize ensures
1974
2080
        # that.
1975
2081
        if isinstance(a_bzrdir, RemoteBzrDir):
1976
2082
            a_bzrdir._ensure_real()
1977
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
 
2083
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
 
2084
                name)
1978
2085
        else:
1979
2086
            # We assume the bzrdir is parameterised; it may not be.
1980
 
            result = self._custom_format.initialize(a_bzrdir)
 
2087
            result = self._custom_format.initialize(a_bzrdir, name)
1981
2088
        if (isinstance(a_bzrdir, RemoteBzrDir) and
1982
2089
            not isinstance(result, RemoteBranch)):
1983
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
 
2090
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
 
2091
                                  name=name)
1984
2092
        return result
1985
2093
 
1986
 
    def initialize(self, a_bzrdir):
 
2094
    def initialize(self, a_bzrdir, name=None):
1987
2095
        # 1) get the network name to use.
1988
2096
        if self._custom_format:
1989
2097
            network_name = self._custom_format.network_name()
1995
2103
            network_name = reference_format.network_name()
1996
2104
        # Being asked to create on a non RemoteBzrDir:
1997
2105
        if not isinstance(a_bzrdir, RemoteBzrDir):
1998
 
            return self._vfs_initialize(a_bzrdir)
 
2106
            return self._vfs_initialize(a_bzrdir, name=name)
1999
2107
        medium = a_bzrdir._client._medium
2000
2108
        if medium._is_remote_before((1, 13)):
2001
 
            return self._vfs_initialize(a_bzrdir)
 
2109
            return self._vfs_initialize(a_bzrdir, name=name)
2002
2110
        # Creating on a remote bzr dir.
2003
2111
        # 2) try direct creation via RPC
2004
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)
2005
2116
        verb = 'BzrDir.create_branch'
2006
2117
        try:
2007
2118
            response = a_bzrdir._call(verb, path, network_name)
2008
2119
        except errors.UnknownSmartMethod:
2009
2120
            # Fallback - use vfs methods
2010
2121
            medium._remember_remote_is_before((1, 13))
2011
 
            return self._vfs_initialize(a_bzrdir)
 
2122
            return self._vfs_initialize(a_bzrdir, name=name)
2012
2123
        if response[0] != 'ok':
2013
2124
            raise errors.UnexpectedSmartServerResponse(response)
2014
2125
        # Turn the response into a RemoteRepository object.
2022
2133
                a_bzrdir._client)
2023
2134
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2024
2135
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2025
 
            format=format, setup_stacking=False)
 
2136
            format=format, setup_stacking=False, name=name)
2026
2137
        # XXX: We know this is a new branch, so it must have revno 0, revid
2027
2138
        # NULL_REVISION. Creating the branch locked would make this be unable
2028
2139
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2048
2159
        return self._custom_format.supports_set_append_revisions_only()
2049
2160
 
2050
2161
 
2051
 
class RemoteBranch(branch.Branch, _RpcHelper):
 
2162
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2052
2163
    """Branch stored on a server accessed by HPSS RPC.
2053
2164
 
2054
2165
    At the moment most operations are mapped down to simple file operations.
2055
2166
    """
2056
2167
 
2057
2168
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2058
 
        _client=None, format=None, setup_stacking=True):
 
2169
        _client=None, format=None, setup_stacking=True, name=None):
2059
2170
        """Create a RemoteBranch instance.
2060
2171
 
2061
2172
        :param real_branch: An optional local implementation of the branch
2067
2178
        :param setup_stacking: If True make an RPC call to determine the
2068
2179
            stacked (or not) status of the branch. If False assume the branch
2069
2180
            is not stacked.
 
2181
        :param name: Colocated branch name
2070
2182
        """
2071
2183
        # We intentionally don't call the parent class's __init__, because it
2072
2184
        # will try to assign to self.tags, which is a property in this subclass.
2091
2203
            self._real_branch = None
2092
2204
        # Fill out expected attributes of branch for bzrlib API users.
2093
2205
        self._clear_cached_state()
2094
 
        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
2095
2209
        self._control_files = None
2096
2210
        self._lock_mode = None
2097
2211
        self._lock_token = None
2162
2276
                    'to use vfs implementation')
2163
2277
            self.bzrdir._ensure_real()
2164
2278
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2165
 
                ignore_fallbacks=self._real_ignore_fallbacks)
 
2279
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
2166
2280
            if self.repository._real_repository is None:
2167
2281
                # Give the remote repository the matching real repo.
2168
2282
                real_repo = self._real_branch.repository
2272
2386
        medium = self._client._medium
2273
2387
        if medium._is_remote_before((1, 18)):
2274
2388
            self._vfs_set_tags_bytes(bytes)
 
2389
            return
2275
2390
        try:
2276
2391
            args = (
2277
2392
                self._remote_path(), self._lock_token, self._repo_lock_token)
2282
2397
            self._vfs_set_tags_bytes(bytes)
2283
2398
 
2284
2399
    def lock_read(self):
 
2400
        """Lock the branch for read operations.
 
2401
 
 
2402
        :return: A bzrlib.lock.LogicalLockResult.
 
2403
        """
2285
2404
        self.repository.lock_read()
2286
2405
        if not self._lock_mode:
 
2406
            self._note_lock('r')
2287
2407
            self._lock_mode = 'r'
2288
2408
            self._lock_count = 1
2289
2409
            if self._real_branch is not None:
2290
2410
                self._real_branch.lock_read()
2291
2411
        else:
2292
2412
            self._lock_count += 1
 
2413
        return lock.LogicalLockResult(self.unlock)
2293
2414
 
2294
2415
    def _remote_lock_write(self, token):
2295
2416
        if token is None:
2296
2417
            branch_token = repo_token = ''
2297
2418
        else:
2298
2419
            branch_token = token
2299
 
            repo_token = self.repository.lock_write()
 
2420
            repo_token = self.repository.lock_write().repository_token
2300
2421
            self.repository.unlock()
2301
2422
        err_context = {'token': token}
2302
 
        response = self._call(
2303
 
            'Branch.lock_write', self._remote_path(), branch_token,
2304
 
            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])
2305
2433
        if response[0] != 'ok':
2306
2434
            raise errors.UnexpectedSmartServerResponse(response)
2307
2435
        ok, branch_token, repo_token = response
2309
2437
 
2310
2438
    def lock_write(self, token=None):
2311
2439
        if not self._lock_mode:
 
2440
            self._note_lock('w')
2312
2441
            # Lock the branch and repo in one remote call.
2313
2442
            remote_tokens = self._remote_lock_write(token)
2314
2443
            self._lock_token, self._repo_lock_token = remote_tokens
2327
2456
            self._lock_mode = 'w'
2328
2457
            self._lock_count = 1
2329
2458
        elif self._lock_mode == 'r':
2330
 
            raise errors.ReadOnlyTransaction
 
2459
            raise errors.ReadOnlyError(self)
2331
2460
        else:
2332
2461
            if token is not None:
2333
2462
                # A token was given to lock_write, and we're relocking, so
2338
2467
            self._lock_count += 1
2339
2468
            # Re-lock the repository too.
2340
2469
            self.repository.lock_write(self._repo_lock_token)
2341
 
        return self._lock_token or None
 
2470
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
2342
2471
 
2343
2472
    def _unlock(self, branch_token, repo_token):
2344
2473
        err_context = {'token': str((branch_token, repo_token))}
2349
2478
            return
2350
2479
        raise errors.UnexpectedSmartServerResponse(response)
2351
2480
 
 
2481
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2352
2482
    def unlock(self):
2353
2483
        try:
2354
2484
            self._lock_count -= 1
2394
2524
            raise NotImplementedError(self.dont_leave_lock_in_place)
2395
2525
        self._leave_lock = False
2396
2526
 
 
2527
    @needs_read_lock
2397
2528
    def get_rev_id(self, revno, history=None):
2398
2529
        if revno == 0:
2399
2530
            return _mod_revision.NULL_REVISION
2665
2796
        medium = self._branch._client._medium
2666
2797
        if medium._is_remote_before((1, 14)):
2667
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):
2668
2807
        try:
2669
2808
            path = self._branch._remote_path()
2670
2809
            response = self._branch._client.call('Branch.set_config_option',
2671
2810
                path, self._branch._lock_token, self._branch._repo_lock_token,
2672
2811
                value.encode('utf8'), name, section or '')
2673
2812
        except errors.UnknownSmartMethod:
 
2813
            medium = self._branch._client._medium
2674
2814
            medium._remember_remote_is_before((1, 14))
2675
2815
            return self._vfs_set_option(value, name, section)
2676
2816
        if response != ():
2677
2817
            raise errors.UnexpectedSmartServerResponse(response)
2678
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
 
2679
2844
    def _real_object(self):
2680
2845
        self._branch._ensure_real()
2681
2846
        return self._branch._real_branch
2764
2929
                    'Missing key %r in context %r', key_err.args[0], context)
2765
2930
                raise err
2766
2931
 
2767
 
    if err.error_verb == 'NoSuchRevision':
 
2932
    if err.error_verb == 'IncompatibleRepositories':
 
2933
        raise errors.IncompatibleRepositories(err.error_args[0],
 
2934
            err.error_args[1], err.error_args[2])
 
2935
    elif err.error_verb == 'NoSuchRevision':
2768
2936
        raise NoSuchRevision(find('branch'), err.error_args[0])
2769
2937
    elif err.error_verb == 'nosuchrevision':
2770
2938
        raise NoSuchRevision(find('repository'), err.error_args[0])
2771
 
    elif err.error_tuple == ('nobranch',):
2772
 
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
 
2939
    elif err.error_verb == 'nobranch':
 
2940
        if len(err.error_args) >= 1:
 
2941
            extra = err.error_args[0]
 
2942
        else:
 
2943
            extra = None
 
2944
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
2945
            detail=extra)
2773
2946
    elif err.error_verb == 'norepository':
2774
2947
        raise errors.NoRepositoryPresent(find('bzrdir'))
2775
2948
    elif err.error_verb == 'LockContention':