/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: John Arbash Meinel
  • Date: 2009-06-04 17:12:29 UTC
  • mto: This revision was merged to the branch mainline in revision 4410.
  • Revision ID: john@arbash-meinel.com-20090604171229-kbgfatt63y3u3uh1
Some small tweaks to decoding strings (avoid passing over the length 2x)

Down to 1.1s (from 1.4s) for decoding all of bzr.dev.
Also, favor decoding strings and then lists in _decode_object, since that is the
frequency we have those types inside Revisions.

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 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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
# TODO: At some point, handle upgrades by just passing the whole request
 
18
# across to run on the server.
 
19
 
17
20
import bz2
18
21
 
19
22
from bzrlib import (
24
27
    debug,
25
28
    errors,
26
29
    graph,
27
 
    lock,
28
30
    lockdir,
 
31
    pack,
29
32
    repository,
30
 
    repository as _mod_repository,
31
33
    revision,
32
34
    revision as _mod_revision,
33
 
    static_tuple,
34
35
    symbol_versioning,
 
36
    urlutils,
35
37
)
36
 
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
38
from bzrlib.branch import BranchReferenceFormat
37
39
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
38
 
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
40
from bzrlib.decorators import needs_read_lock, needs_write_lock
39
41
from bzrlib.errors import (
40
42
    NoSuchRevision,
41
43
    SmartProtocolError,
43
45
from bzrlib.lockable_files import LockableFiles
44
46
from bzrlib.smart import client, vfs, repository as smart_repo
45
47
from bzrlib.revision import ensure_null, NULL_REVISION
46
 
from bzrlib.repository import RepositoryWriteLockResult
47
48
from bzrlib.trace import mutter, note, warning
48
49
 
49
50
 
62
63
        except errors.ErrorFromSmartServer, err:
63
64
            self._translate_error(err, **err_context)
64
65
 
65
 
    def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
66
 
        try:
67
 
            return self._client.call_with_body_bytes(method, args, body_bytes)
68
 
        except errors.ErrorFromSmartServer, err:
69
 
            self._translate_error(err, **err_context)
70
 
 
71
66
    def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
72
67
                                             **err_context):
73
68
        try:
92
87
class RemoteBzrDir(BzrDir, _RpcHelper):
93
88
    """Control directory on a remote server, accessed via bzr:// or similar."""
94
89
 
95
 
    def __init__(self, transport, format, _client=None, _force_probe=False):
 
90
    def __init__(self, transport, format, _client=None):
96
91
        """Construct a RemoteBzrDir.
97
92
 
98
93
        :param _client: Private parameter for testing. Disables probing and the
102
97
        # this object holds a delegated bzrdir that uses file-level operations
103
98
        # to talk to the other side
104
99
        self._real_bzrdir = None
105
 
        self._has_working_tree = None
106
100
        # 1-shot cache for the call pattern 'create_branch; open_branch' - see
107
101
        # create_branch for details.
108
102
        self._next_open_branch_result = None
112
106
            self._client = client._SmartClient(medium)
113
107
        else:
114
108
            self._client = _client
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
 
109
            return
 
110
 
125
111
        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):
151
112
        response = self._call('BzrDir.open', path)
152
113
        if response not in [('yes',), ('no',)]:
153
114
            raise errors.UnexpectedSmartServerResponse(response)
154
115
        if response == ('no',):
155
 
            raise errors.NotBranchError(path=self.root_transport.base)
 
116
            raise errors.NotBranchError(path=transport.base)
156
117
 
157
118
    def _ensure_real(self):
158
119
        """Ensure that there is a _real_bzrdir set.
160
121
        Used before calls to self._real_bzrdir.
161
122
        """
162
123
        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()))
167
124
            self._real_bzrdir = BzrDir.open_from_transport(
168
125
                self.root_transport, _server_formats=False)
169
126
            self._format._network_name = \
245
202
        self._ensure_real()
246
203
        self._real_bzrdir.destroy_repository()
247
204
 
248
 
    def create_branch(self, name=None):
 
205
    def create_branch(self):
249
206
        # as per meta1 formats - just delegate to the format object which may
250
207
        # be parameterised.
251
 
        real_branch = self._format.get_branch_format().initialize(self,
252
 
            name=name)
 
208
        real_branch = self._format.get_branch_format().initialize(self)
253
209
        if not isinstance(real_branch, RemoteBranch):
254
 
            result = RemoteBranch(self, self.find_repository(), real_branch,
255
 
                                  name=name)
 
210
            result = RemoteBranch(self, self.find_repository(), real_branch)
256
211
        else:
257
212
            result = real_branch
258
213
        # BzrDir.clone_on_transport() uses the result of create_branch but does
264
219
        self._next_open_branch_result = result
265
220
        return result
266
221
 
267
 
    def destroy_branch(self, name=None):
 
222
    def destroy_branch(self):
268
223
        """See BzrDir.destroy_branch"""
269
224
        self._ensure_real()
270
 
        self._real_bzrdir.destroy_branch(name=name)
 
225
        self._real_bzrdir.destroy_branch()
271
226
        self._next_open_branch_result = None
272
227
 
273
228
    def create_workingtree(self, revision_id=None, from_branch=None):
292
247
    def _get_branch_reference(self):
293
248
        path = self._path_for_remote_call(self._client)
294
249
        medium = self._client._medium
295
 
        candidate_calls = [
296
 
            ('BzrDir.open_branchV3', (2, 1)),
297
 
            ('BzrDir.open_branchV2', (1, 13)),
298
 
            ('BzrDir.open_branch', None),
299
 
            ]
300
 
        for verb, required_version in candidate_calls:
301
 
            if required_version and medium._is_remote_before(required_version):
302
 
                continue
 
250
        if not medium._is_remote_before((1, 13)):
303
251
            try:
304
 
                response = self._call(verb, path)
 
252
                response = self._call('BzrDir.open_branchV2', path)
 
253
                if response[0] not in ('ref', 'branch'):
 
254
                    raise errors.UnexpectedSmartServerResponse(response)
 
255
                return response
305
256
            except errors.UnknownSmartMethod:
306
 
                if required_version is None:
307
 
                    raise
308
 
                medium._remember_remote_is_before(required_version)
309
 
            else:
310
 
                break
311
 
        if verb == 'BzrDir.open_branch':
312
 
            if response[0] != 'ok':
313
 
                raise errors.UnexpectedSmartServerResponse(response)
314
 
            if response[1] != '':
315
 
                return ('ref', response[1])
316
 
            else:
317
 
                return ('branch', '')
318
 
        if response[0] not in ('ref', 'branch'):
 
257
                medium._remember_remote_is_before((1, 13))
 
258
        response = self._call('BzrDir.open_branch', path)
 
259
        if response[0] != 'ok':
319
260
            raise errors.UnexpectedSmartServerResponse(response)
320
 
        return response
 
261
        if response[1] != '':
 
262
            return ('ref', response[1])
 
263
        else:
 
264
            return ('branch', '')
321
265
 
322
266
    def _get_tree_branch(self):
323
267
        """See BzrDir._get_tree_branch()."""
324
268
        return None, self.open_branch()
325
269
 
326
 
    def open_branch(self, name=None, unsupported=False,
327
 
                    ignore_fallbacks=False):
328
 
        if unsupported:
 
270
    def open_branch(self, _unsupported=False, ignore_fallbacks=False):
 
271
        if _unsupported:
329
272
            raise NotImplementedError('unsupported flag support not implemented yet.')
330
273
        if self._next_open_branch_result is not None:
331
274
            # See create_branch for details.
336
279
        if response[0] == 'ref':
337
280
            # a branch reference, use the existing BranchReference logic.
338
281
            format = BranchReferenceFormat()
339
 
            return format.open(self, name=name, _found=True,
340
 
                location=response[1], ignore_fallbacks=ignore_fallbacks)
 
282
            return format.open(self, _found=True, location=response[1],
 
283
                ignore_fallbacks=ignore_fallbacks)
341
284
        branch_format_name = response[1]
342
285
        if not branch_format_name:
343
286
            branch_format_name = None
344
287
        format = RemoteBranchFormat(network_name=branch_format_name)
345
288
        return RemoteBranch(self, self.find_repository(), format=format,
346
 
            setup_stacking=not ignore_fallbacks, name=name)
 
289
            setup_stacking=not ignore_fallbacks)
347
290
 
348
291
    def _open_repo_v1(self, path):
349
292
        verb = 'BzrDir.find_repository'
410
353
        else:
411
354
            raise errors.NoRepositoryPresent(self)
412
355
 
413
 
    def has_workingtree(self):
414
 
        if self._has_working_tree is None:
415
 
            self._ensure_real()
416
 
            self._has_working_tree = self._real_bzrdir.has_workingtree()
417
 
        return self._has_working_tree
418
 
 
419
356
    def open_workingtree(self, recommend_upgrade=True):
420
 
        if self.has_workingtree():
 
357
        self._ensure_real()
 
358
        if self._real_bzrdir.has_workingtree():
421
359
            raise errors.NotLocalUrl(self.root_transport)
422
360
        else:
423
361
            raise errors.NoWorkingTree(self.root_transport.base)
426
364
        """Return the path to be used for this bzrdir in a remote call."""
427
365
        return client.remote_path_from_transport(self.root_transport)
428
366
 
429
 
    def get_branch_transport(self, branch_format, name=None):
 
367
    def get_branch_transport(self, branch_format):
430
368
        self._ensure_real()
431
 
        return self._real_bzrdir.get_branch_transport(branch_format, name=name)
 
369
        return self._real_bzrdir.get_branch_transport(branch_format)
432
370
 
433
371
    def get_repository_transport(self, repository_format):
434
372
        self._ensure_real()
486
424
        self._custom_format = None
487
425
        self._network_name = None
488
426
        self._creating_bzrdir = None
489
 
        self._supports_chks = None
490
427
        self._supports_external_lookups = None
491
428
        self._supports_tree_reference = None
492
429
        self._rich_root_data = None
493
430
 
494
 
    def __repr__(self):
495
 
        return "%s(_network_name=%r)" % (self.__class__.__name__,
496
 
            self._network_name)
497
 
 
498
431
    @property
499
432
    def fast_deltas(self):
500
433
        self._ensure_real()
508
441
        return self._rich_root_data
509
442
 
510
443
    @property
511
 
    def supports_chks(self):
512
 
        if self._supports_chks is None:
513
 
            self._ensure_real()
514
 
            self._supports_chks = self._custom_format.supports_chks
515
 
        return self._supports_chks
516
 
 
517
 
    @property
518
444
    def supports_external_lookups(self):
519
445
        if self._supports_external_lookups is None:
520
446
            self._ensure_real()
621
547
        return self._custom_format._fetch_reconcile
622
548
 
623
549
    def get_format_description(self):
624
 
        self._ensure_real()
625
 
        return 'Remote: ' + self._custom_format.get_format_description()
 
550
        return 'bzr remote repository'
626
551
 
627
552
    def __eq__(self, other):
628
553
        return self.__class__ is other.__class__
629
554
 
 
555
    def check_conversion_target(self, target_format):
 
556
        if self.rich_root_data and not target_format.rich_root_data:
 
557
            raise errors.BadConversionTarget(
 
558
                'Does not support rich root data.', target_format)
 
559
        if (self.supports_tree_reference and
 
560
            not getattr(target_format, 'supports_tree_reference', False)):
 
561
            raise errors.BadConversionTarget(
 
562
                'Does not support nested trees', target_format)
 
563
 
630
564
    def network_name(self):
631
565
        if self._network_name:
632
566
            return self._network_name
634
568
        return self._creating_repo._real_repository._format.network_name()
635
569
 
636
570
    @property
637
 
    def pack_compresses(self):
638
 
        self._ensure_real()
639
 
        return self._custom_format.pack_compresses
640
 
 
641
 
    @property
642
571
    def _serializer(self):
643
572
        self._ensure_real()
644
573
        return self._custom_format._serializer
645
574
 
646
575
 
647
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
648
 
    bzrdir.ControlComponent):
 
576
class RemoteRepository(_RpcHelper):
649
577
    """Repository accessed over rpc.
650
578
 
651
579
    For the moment most operations are performed using local transport-backed
694
622
        # Additional places to query for data.
695
623
        self._fallback_repositories = []
696
624
 
697
 
    @property
698
 
    def user_transport(self):
699
 
        return self.bzrdir.user_transport
700
 
 
701
 
    @property
702
 
    def control_transport(self):
703
 
        # XXX: Normally you shouldn't directly get at the remote repository
704
 
        # transport, but I'm not sure it's worth making this method
705
 
        # optional -- mbp 2010-04-21
706
 
        return self.bzrdir.get_repository_transport(None)
707
 
        
708
625
    def __str__(self):
709
626
        return "%s(%s)" % (self.__class__.__name__, self.base)
710
627
 
758
675
        return self._real_repository.get_missing_parent_inventories(
759
676
            check_for_missing_texts=check_for_missing_texts)
760
677
 
761
 
    def _get_rev_id_for_revno_vfs(self, revno, known_pair):
762
 
        self._ensure_real()
763
 
        return self._real_repository.get_rev_id_for_revno(
764
 
            revno, known_pair)
765
 
 
766
 
    def get_rev_id_for_revno(self, revno, known_pair):
767
 
        """See Repository.get_rev_id_for_revno."""
768
 
        path = self.bzrdir._path_for_remote_call(self._client)
769
 
        try:
770
 
            if self._client._medium._is_remote_before((1, 17)):
771
 
                return self._get_rev_id_for_revno_vfs(revno, known_pair)
772
 
            response = self._call(
773
 
                'Repository.get_rev_id_for_revno', path, revno, known_pair)
774
 
        except errors.UnknownSmartMethod:
775
 
            self._client._medium._remember_remote_is_before((1, 17))
776
 
            return self._get_rev_id_for_revno_vfs(revno, known_pair)
777
 
        if response[0] == 'ok':
778
 
            return True, response[1]
779
 
        elif response[0] == 'history-incomplete':
780
 
            known_pair = response[1:3]
781
 
            for fallback in self._fallback_repositories:
782
 
                found, result = fallback.get_rev_id_for_revno(revno, known_pair)
783
 
                if found:
784
 
                    return True, result
785
 
                else:
786
 
                    known_pair = result
787
 
            # Not found in any fallbacks
788
 
            return False, known_pair
789
 
        else:
790
 
            raise errors.UnexpectedSmartServerResponse(response)
791
 
 
792
678
    def _ensure_real(self):
793
679
        """Ensure that there is a _real_repository set.
794
680
 
803
689
        invocation. If in doubt chat to the bzr network team.
804
690
        """
805
691
        if self._real_repository is None:
806
 
            if 'hpssvfs' in debug.debug_flags:
 
692
            if 'hpss' in debug.debug_flags:
807
693
                import traceback
808
694
                warning('VFS Repository access triggered\n%s',
809
695
                    ''.join(traceback.format_stack()))
893
779
            result.add(_mod_revision.NULL_REVISION)
894
780
        return result
895
781
 
896
 
    def _has_same_fallbacks(self, other_repo):
897
 
        """Returns true if the repositories have the same fallbacks."""
898
 
        # XXX: copied from Repository; it should be unified into a base class
899
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
900
 
        my_fb = self._fallback_repositories
901
 
        other_fb = other_repo._fallback_repositories
902
 
        if len(my_fb) != len(other_fb):
903
 
            return False
904
 
        for f, g in zip(my_fb, other_fb):
905
 
            if not f.has_same_location(g):
906
 
                return False
907
 
        return True
908
 
 
909
782
    def has_same_location(self, other):
910
 
        # TODO: Move to RepositoryBase and unify with the regular Repository
911
 
        # one; unfortunately the tests rely on slightly different behaviour at
912
 
        # present -- mbp 20090710
913
783
        return (self.__class__ is other.__class__ and
914
784
                self.bzrdir.transport.base == other.bzrdir.transport.base)
915
785
 
918
788
        parents_provider = self._make_parents_provider(other_repository)
919
789
        return graph.Graph(parents_provider)
920
790
 
921
 
    @needs_read_lock
922
 
    def get_known_graph_ancestry(self, revision_ids):
923
 
        """Return the known graph for a set of revision ids and their ancestors.
924
 
        """
925
 
        st = static_tuple.StaticTuple
926
 
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
927
 
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
928
 
        return graph.GraphThunkIdsToKeys(known_graph)
929
 
 
930
791
    def gather_stats(self, revid=None, committers=None):
931
792
        """See Repository.gather_stats()."""
932
793
        path = self.bzrdir._path_for_remote_call(self._client)
992
853
    def is_write_locked(self):
993
854
        return self._lock_mode == 'w'
994
855
 
995
 
    def _warn_if_deprecated(self, branch=None):
996
 
        # If we have a real repository, the check will be done there, if we
997
 
        # don't the check will be done remotely.
998
 
        pass
999
 
 
1000
856
    def lock_read(self):
1001
 
        """Lock the repository for read operations.
1002
 
 
1003
 
        :return: An object with an unlock method which will release the lock
1004
 
            obtained.
1005
 
        """
1006
857
        # wrong eventually - want a local lock cache context
1007
858
        if not self._lock_mode:
1008
 
            self._note_lock('r')
1009
859
            self._lock_mode = 'r'
1010
860
            self._lock_count = 1
1011
861
            self._unstacked_provider.enable_cache(cache_misses=True)
1015
865
                repo.lock_read()
1016
866
        else:
1017
867
            self._lock_count += 1
1018
 
        return self
1019
868
 
1020
869
    def _remote_lock_write(self, token):
1021
870
        path = self.bzrdir._path_for_remote_call(self._client)
1032
881
 
1033
882
    def lock_write(self, token=None, _skip_rpc=False):
1034
883
        if not self._lock_mode:
1035
 
            self._note_lock('w')
1036
884
            if _skip_rpc:
1037
885
                if self._lock_token is not None:
1038
886
                    if token != self._lock_token:
1061
909
            raise errors.ReadOnlyError(self)
1062
910
        else:
1063
911
            self._lock_count += 1
1064
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
912
        return self._lock_token or None
1065
913
 
1066
914
    def leave_lock_in_place(self):
1067
915
        if not self._lock_token:
1141
989
        else:
1142
990
            raise errors.UnexpectedSmartServerResponse(response)
1143
991
 
1144
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
1145
992
    def unlock(self):
1146
993
        if not self._lock_count:
1147
 
            return lock.cant_unlock_not_held(self)
 
994
            raise errors.LockNotHeld(self)
1148
995
        self._lock_count -= 1
1149
996
        if self._lock_count > 0:
1150
997
            return
1247
1094
            # state, so always add a lock here. If a caller passes us a locked
1248
1095
            # repository, they are responsible for unlocking it later.
1249
1096
            repository.lock_read()
1250
 
        self._check_fallback_repository(repository)
1251
1097
        self._fallback_repositories.append(repository)
1252
1098
        # If self._real_repository was parameterised already (e.g. because a
1253
1099
        # _real_branch had its get_stacked_on_url method called), then the
1254
1100
        # repository to be added may already be in the _real_repositories list.
1255
1101
        if self._real_repository is not None:
1256
 
            fallback_locations = [repo.user_url for repo in
 
1102
            fallback_locations = [repo.bzrdir.root_transport.base for repo in
1257
1103
                self._real_repository._fallback_repositories]
1258
 
            if repository.user_url not in fallback_locations:
 
1104
            if repository.bzrdir.root_transport.base not in fallback_locations:
1259
1105
                self._real_repository.add_fallback_repository(repository)
1260
1106
 
1261
 
    def _check_fallback_repository(self, repository):
1262
 
        """Check that this repository can fallback to repository safely.
1263
 
 
1264
 
        Raise an error if not.
1265
 
 
1266
 
        :param repository: A repository to fallback to.
1267
 
        """
1268
 
        return _mod_repository.InterRepository._assert_same_model(
1269
 
            self, repository)
1270
 
 
1271
1107
    def add_inventory(self, revid, inv, parents):
1272
1108
        self._ensure_real()
1273
1109
        return self._real_repository.add_inventory(revid, inv, parents)
1274
1110
 
1275
1111
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1276
 
            parents, basis_inv=None, propagate_caches=False):
 
1112
                               parents):
1277
1113
        self._ensure_real()
1278
1114
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
1279
 
            delta, new_revision_id, parents, basis_inv=basis_inv,
1280
 
            propagate_caches=propagate_caches)
 
1115
            delta, new_revision_id, parents)
1281
1116
 
1282
1117
    def add_revision(self, rev_id, rev, inv=None, config=None):
1283
1118
        self._ensure_real()
1289
1124
        self._ensure_real()
1290
1125
        return self._real_repository.get_inventory(revision_id)
1291
1126
 
1292
 
    def iter_inventories(self, revision_ids, ordering=None):
 
1127
    def iter_inventories(self, revision_ids):
1293
1128
        self._ensure_real()
1294
 
        return self._real_repository.iter_inventories(revision_ids, ordering)
 
1129
        return self._real_repository.iter_inventories(revision_ids)
1295
1130
 
1296
1131
    @needs_read_lock
1297
1132
    def get_revision(self, revision_id):
1361
1196
            raise errors.InternalBzrError(
1362
1197
                "May not fetch while in a write group.")
1363
1198
        # fast path same-url fetch operations
1364
 
        if (self.has_same_location(source)
1365
 
            and fetch_spec is None
1366
 
            and self._has_same_fallbacks(source)):
 
1199
        if self.has_same_location(source) and fetch_spec is None:
1367
1200
            # check that last_revision is in 'from' and then return a
1368
1201
            # no-operation.
1369
1202
            if (revision_id is not None and
1542
1375
        return self._real_repository.get_signature_text(revision_id)
1543
1376
 
1544
1377
    @needs_read_lock
1545
 
    def _get_inventory_xml(self, revision_id):
1546
 
        self._ensure_real()
1547
 
        return self._real_repository._get_inventory_xml(revision_id)
 
1378
    def get_inventory_xml(self, revision_id):
 
1379
        self._ensure_real()
 
1380
        return self._real_repository.get_inventory_xml(revision_id)
 
1381
 
 
1382
    def deserialise_inventory(self, revision_id, xml):
 
1383
        self._ensure_real()
 
1384
        return self._real_repository.deserialise_inventory(revision_id, xml)
1548
1385
 
1549
1386
    def reconcile(self, other=None, thorough=False):
1550
1387
        self._ensure_real()
1577
1414
        return self._real_repository.get_revision_reconcile(revision_id)
1578
1415
 
1579
1416
    @needs_read_lock
1580
 
    def check(self, revision_ids=None, callback_refs=None, check_repo=True):
 
1417
    def check(self, revision_ids=None):
1581
1418
        self._ensure_real()
1582
 
        return self._real_repository.check(revision_ids=revision_ids,
1583
 
            callback_refs=callback_refs, check_repo=check_repo)
 
1419
        return self._real_repository.check(revision_ids=revision_ids)
1584
1420
 
1585
1421
    def copy_content_into(self, destination, revision_id=None):
1586
1422
        self._ensure_real()
1626
1462
        return self._real_repository.inventories
1627
1463
 
1628
1464
    @needs_write_lock
1629
 
    def pack(self, hint=None, clean_obsolete_packs=False):
 
1465
    def pack(self):
1630
1466
        """Compress the data within the repository.
1631
1467
 
1632
1468
        This is not currently implemented within the smart server.
1633
1469
        """
1634
1470
        self._ensure_real()
1635
 
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
 
1471
        return self._real_repository.pack()
1636
1472
 
1637
1473
    @property
1638
1474
    def revisions(self):
1726
1562
        self._ensure_real()
1727
1563
        return self._real_repository.revision_graph_can_have_wrong_parents()
1728
1564
 
1729
 
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
 
1565
    def _find_inconsistent_revision_parents(self):
1730
1566
        self._ensure_real()
1731
 
        return self._real_repository._find_inconsistent_revision_parents(
1732
 
            revisions_iterator)
 
1567
        return self._real_repository._find_inconsistent_revision_parents()
1733
1568
 
1734
1569
    def _check_for_inconsistent_revision_parents(self):
1735
1570
        self._ensure_real()
1741
1576
            providers.insert(0, other)
1742
1577
        providers.extend(r._make_parents_provider() for r in
1743
1578
                         self._fallback_repositories)
1744
 
        return graph.StackedParentsProvider(providers)
 
1579
        return graph._StackedParentsProvider(providers)
1745
1580
 
1746
1581
    def _serialise_search_recipe(self, recipe):
1747
1582
        """Serialise a graph search recipe.
1789
1624
    def insert_stream(self, stream, src_format, resume_tokens):
1790
1625
        target = self.target_repo
1791
1626
        target._unstacked_provider.missing_keys.clear()
1792
 
        candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
1793
1627
        if target._lock_token:
1794
 
            candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
1795
 
            lock_args = (target._lock_token or '',)
 
1628
            verb = 'Repository.insert_stream_locked'
 
1629
            extra_args = (target._lock_token or '',)
 
1630
            required_version = (1, 14)
1796
1631
        else:
1797
 
            candidate_calls.append(('Repository.insert_stream', (1, 13)))
1798
 
            lock_args = ()
 
1632
            verb = 'Repository.insert_stream'
 
1633
            extra_args = ()
 
1634
            required_version = (1, 13)
1799
1635
        client = target._client
1800
1636
        medium = client._medium
 
1637
        if medium._is_remote_before(required_version):
 
1638
            # No possible way this can work.
 
1639
            return self._insert_real(stream, src_format, resume_tokens)
1801
1640
        path = target.bzrdir._path_for_remote_call(client)
1802
 
        # Probe for the verb to use with an empty stream before sending the
1803
 
        # real stream to it.  We do this both to avoid the risk of sending a
1804
 
        # large request that is then rejected, and because we don't want to
1805
 
        # implement a way to buffer, rewind, or restart the stream.
1806
 
        found_verb = False
1807
 
        for verb, required_version in candidate_calls:
1808
 
            if medium._is_remote_before(required_version):
1809
 
                continue
1810
 
            if resume_tokens:
1811
 
                # We've already done the probing (and set _is_remote_before) on
1812
 
                # a previous insert.
1813
 
                found_verb = True
1814
 
                break
 
1641
        if not resume_tokens:
 
1642
            # XXX: Ugly but important for correctness, *will* be fixed during
 
1643
            # 1.13 cycle. Pushing a stream that is interrupted results in a
 
1644
            # fallback to the _real_repositories sink *with a partial stream*.
 
1645
            # Thats bad because we insert less data than bzr expected. To avoid
 
1646
            # this we do a trial push to make sure the verb is accessible, and
 
1647
            # do not fallback when actually pushing the stream. A cleanup patch
 
1648
            # is going to look at rewinding/restarting the stream/partial
 
1649
            # buffering etc.
1815
1650
            byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1816
1651
            try:
1817
1652
                response = client.call_with_body_stream(
1818
 
                    (verb, path, '') + lock_args, byte_stream)
 
1653
                    (verb, path, '') + extra_args, byte_stream)
1819
1654
            except errors.UnknownSmartMethod:
1820
1655
                medium._remember_remote_is_before(required_version)
1821
 
            else:
1822
 
                found_verb = True
1823
 
                break
1824
 
        if not found_verb:
1825
 
            # Have to use VFS.
1826
 
            return self._insert_real(stream, src_format, resume_tokens)
1827
 
        self._last_inv_record = None
1828
 
        self._last_substream = None
1829
 
        if required_version < (1, 19):
1830
 
            # Remote side doesn't support inventory deltas.  Wrap the stream to
1831
 
            # make sure we don't send any.  If the stream contains inventory
1832
 
            # deltas we'll interrupt the smart insert_stream request and
1833
 
            # fallback to VFS.
1834
 
            stream = self._stop_stream_if_inventory_delta(stream)
 
1656
                return self._insert_real(stream, src_format, resume_tokens)
1835
1657
        byte_stream = smart_repo._stream_to_byte_stream(
1836
1658
            stream, src_format)
1837
1659
        resume_tokens = ' '.join(resume_tokens)
1838
1660
        response = client.call_with_body_stream(
1839
 
            (verb, path, resume_tokens) + lock_args, byte_stream)
 
1661
            (verb, path, resume_tokens) + extra_args, byte_stream)
1840
1662
        if response[0][0] not in ('ok', 'missing-basis'):
1841
1663
            raise errors.UnexpectedSmartServerResponse(response)
1842
 
        if self._last_substream is not None:
1843
 
            # The stream included an inventory-delta record, but the remote
1844
 
            # side isn't new enough to support them.  So we need to send the
1845
 
            # rest of the stream via VFS.
1846
 
            self.target_repo.refresh_data()
1847
 
            return self._resume_stream_with_vfs(response, src_format)
1848
1664
        if response[0][0] == 'missing-basis':
1849
1665
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1850
1666
            resume_tokens = tokens
1853
1669
            self.target_repo.refresh_data()
1854
1670
            return [], set()
1855
1671
 
1856
 
    def _resume_stream_with_vfs(self, response, src_format):
1857
 
        """Resume sending a stream via VFS, first resending the record and
1858
 
        substream that couldn't be sent via an insert_stream verb.
1859
 
        """
1860
 
        if response[0][0] == 'missing-basis':
1861
 
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1862
 
            # Ignore missing_keys, we haven't finished inserting yet
1863
 
        else:
1864
 
            tokens = []
1865
 
        def resume_substream():
1866
 
            # Yield the substream that was interrupted.
1867
 
            for record in self._last_substream:
1868
 
                yield record
1869
 
            self._last_substream = None
1870
 
        def resume_stream():
1871
 
            # Finish sending the interrupted substream
1872
 
            yield ('inventory-deltas', resume_substream())
1873
 
            # Then simply continue sending the rest of the stream.
1874
 
            for substream_kind, substream in self._last_stream:
1875
 
                yield substream_kind, substream
1876
 
        return self._insert_real(resume_stream(), src_format, tokens)
1877
 
 
1878
 
    def _stop_stream_if_inventory_delta(self, stream):
1879
 
        """Normally this just lets the original stream pass-through unchanged.
1880
 
 
1881
 
        However if any 'inventory-deltas' substream occurs it will stop
1882
 
        streaming, and store the interrupted substream and stream in
1883
 
        self._last_substream and self._last_stream so that the stream can be
1884
 
        resumed by _resume_stream_with_vfs.
1885
 
        """
1886
 
                    
1887
 
        stream_iter = iter(stream)
1888
 
        for substream_kind, substream in stream_iter:
1889
 
            if substream_kind == 'inventory-deltas':
1890
 
                self._last_substream = substream
1891
 
                self._last_stream = stream_iter
1892
 
                return
1893
 
            else:
1894
 
                yield substream_kind, substream
1895
 
            
1896
1672
 
1897
1673
class RemoteStreamSource(repository.StreamSource):
1898
1674
    """Stream data from a remote server."""
1901
1677
        if (self.from_repository._fallback_repositories and
1902
1678
            self.to_format._fetch_order == 'topological'):
1903
1679
            return self._real_stream(self.from_repository, search)
1904
 
        sources = []
1905
 
        seen = set()
1906
 
        repos = [self.from_repository]
1907
 
        while repos:
1908
 
            repo = repos.pop(0)
1909
 
            if repo in seen:
1910
 
                continue
1911
 
            seen.add(repo)
1912
 
            repos.extend(repo._fallback_repositories)
1913
 
            sources.append(repo)
1914
 
        return self.missing_parents_chain(search, sources)
1915
 
 
1916
 
    def get_stream_for_missing_keys(self, missing_keys):
1917
 
        self.from_repository._ensure_real()
1918
 
        real_repo = self.from_repository._real_repository
1919
 
        real_source = real_repo._get_source(self.to_format)
1920
 
        return real_source.get_stream_for_missing_keys(missing_keys)
 
1680
        return self.missing_parents_chain(search, [self.from_repository] +
 
1681
            self.from_repository._fallback_repositories)
1921
1682
 
1922
1683
    def _real_stream(self, repo, search):
1923
1684
        """Get a stream for search from repo.
1930
1691
        """
1931
1692
        source = repo._get_source(self.to_format)
1932
1693
        if isinstance(source, RemoteStreamSource):
1933
 
            repo._ensure_real()
1934
 
            source = repo._real_repository._get_source(self.to_format)
 
1694
            return repository.StreamSource.get_stream(source, search)
1935
1695
        return source.get_stream(search)
1936
1696
 
1937
1697
    def _get_stream(self, repo, search):
1954
1714
            return self._real_stream(repo, search)
1955
1715
        client = repo._client
1956
1716
        medium = client._medium
 
1717
        if medium._is_remote_before((1, 13)):
 
1718
            # streaming was added in 1.13
 
1719
            return self._real_stream(repo, search)
1957
1720
        path = repo.bzrdir._path_for_remote_call(client)
1958
 
        search_bytes = repo._serialise_search_result(search)
1959
 
        args = (path, self.to_format.network_name())
1960
 
        candidate_verbs = [
1961
 
            ('Repository.get_stream_1.19', (1, 19)),
1962
 
            ('Repository.get_stream', (1, 13))]
1963
 
        found_verb = False
1964
 
        for verb, version in candidate_verbs:
1965
 
            if medium._is_remote_before(version):
1966
 
                continue
1967
 
            try:
1968
 
                response = repo._call_with_body_bytes_expecting_body(
1969
 
                    verb, args, search_bytes)
1970
 
            except errors.UnknownSmartMethod:
1971
 
                medium._remember_remote_is_before(version)
1972
 
            else:
1973
 
                response_tuple, response_handler = response
1974
 
                found_verb = True
1975
 
                break
1976
 
        if not found_verb:
 
1721
        try:
 
1722
            search_bytes = repo._serialise_search_result(search)
 
1723
            response = repo._call_with_body_bytes_expecting_body(
 
1724
                'Repository.get_stream',
 
1725
                (path, self.to_format.network_name()), search_bytes)
 
1726
            response_tuple, response_handler = response
 
1727
        except errors.UnknownSmartMethod:
 
1728
            medium._remember_remote_is_before((1,13))
1977
1729
            return self._real_stream(repo, search)
1978
1730
        if response_tuple[0] != 'ok':
1979
1731
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1991
1743
        :param search: The overall search to satisfy with streams.
1992
1744
        :param sources: A list of Repository objects to query.
1993
1745
        """
1994
 
        self.from_serialiser = self.from_repository._format._serializer
 
1746
        self.serialiser = self.to_format._serializer
1995
1747
        self.seen_revs = set()
1996
1748
        self.referenced_revs = set()
1997
1749
        # If there are heads in the search, or the key count is > 0, we are not
2014
1766
    def missing_parents_rev_handler(self, substream):
2015
1767
        for content in substream:
2016
1768
            revision_bytes = content.get_bytes_as('fulltext')
2017
 
            revision = self.from_serialiser.read_revision_from_string(
2018
 
                revision_bytes)
 
1769
            revision = self.serialiser.read_revision_from_string(revision_bytes)
2019
1770
            self.seen_revs.add(content.key[-1])
2020
1771
            self.referenced_revs.update(revision.parent_ids)
2021
1772
            yield content
2060
1811
                self._network_name)
2061
1812
 
2062
1813
    def get_format_description(self):
2063
 
        self._ensure_real()
2064
 
        return 'Remote: ' + self._custom_format.get_format_description()
 
1814
        return 'Remote BZR Branch'
2065
1815
 
2066
1816
    def network_name(self):
2067
1817
        return self._network_name
2068
1818
 
2069
 
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2070
 
        return a_bzrdir.open_branch(name=name, 
2071
 
            ignore_fallbacks=ignore_fallbacks)
 
1819
    def open(self, a_bzrdir, ignore_fallbacks=False):
 
1820
        return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2072
1821
 
2073
 
    def _vfs_initialize(self, a_bzrdir, name):
 
1822
    def _vfs_initialize(self, a_bzrdir):
2074
1823
        # Initialisation when using a local bzrdir object, or a non-vfs init
2075
1824
        # method is not available on the server.
2076
1825
        # self._custom_format is always set - the start of initialize ensures
2077
1826
        # that.
2078
1827
        if isinstance(a_bzrdir, RemoteBzrDir):
2079
1828
            a_bzrdir._ensure_real()
2080
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2081
 
                name)
 
1829
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2082
1830
        else:
2083
1831
            # We assume the bzrdir is parameterised; it may not be.
2084
 
            result = self._custom_format.initialize(a_bzrdir, name)
 
1832
            result = self._custom_format.initialize(a_bzrdir)
2085
1833
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2086
1834
            not isinstance(result, RemoteBranch)):
2087
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2088
 
                                  name=name)
 
1835
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2089
1836
        return result
2090
1837
 
2091
 
    def initialize(self, a_bzrdir, name=None):
 
1838
    def initialize(self, a_bzrdir):
2092
1839
        # 1) get the network name to use.
2093
1840
        if self._custom_format:
2094
1841
            network_name = self._custom_format.network_name()
2100
1847
            network_name = reference_format.network_name()
2101
1848
        # Being asked to create on a non RemoteBzrDir:
2102
1849
        if not isinstance(a_bzrdir, RemoteBzrDir):
2103
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
1850
            return self._vfs_initialize(a_bzrdir)
2104
1851
        medium = a_bzrdir._client._medium
2105
1852
        if medium._is_remote_before((1, 13)):
2106
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
1853
            return self._vfs_initialize(a_bzrdir)
2107
1854
        # Creating on a remote bzr dir.
2108
1855
        # 2) try direct creation via RPC
2109
1856
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2110
 
        if name is not None:
2111
 
            # XXX JRV20100304: Support creating colocated branches
2112
 
            raise errors.NoColocatedBranchSupport(self)
2113
1857
        verb = 'BzrDir.create_branch'
2114
1858
        try:
2115
1859
            response = a_bzrdir._call(verb, path, network_name)
2116
1860
        except errors.UnknownSmartMethod:
2117
1861
            # Fallback - use vfs methods
2118
1862
            medium._remember_remote_is_before((1, 13))
2119
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
1863
            return self._vfs_initialize(a_bzrdir)
2120
1864
        if response[0] != 'ok':
2121
1865
            raise errors.UnexpectedSmartServerResponse(response)
2122
1866
        # Turn the response into a RemoteRepository object.
2130
1874
                a_bzrdir._client)
2131
1875
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2132
1876
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2133
 
            format=format, setup_stacking=False, name=name)
 
1877
            format=format, setup_stacking=False)
2134
1878
        # XXX: We know this is a new branch, so it must have revno 0, revid
2135
1879
        # NULL_REVISION. Creating the branch locked would make this be unable
2136
1880
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2151
1895
        self._ensure_real()
2152
1896
        return self._custom_format.supports_stacking()
2153
1897
 
2154
 
    def supports_set_append_revisions_only(self):
2155
 
        self._ensure_real()
2156
 
        return self._custom_format.supports_set_append_revisions_only()
2157
 
 
2158
 
 
2159
 
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
 
1898
 
 
1899
class RemoteBranch(branch.Branch, _RpcHelper):
2160
1900
    """Branch stored on a server accessed by HPSS RPC.
2161
1901
 
2162
1902
    At the moment most operations are mapped down to simple file operations.
2163
1903
    """
2164
1904
 
2165
1905
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2166
 
        _client=None, format=None, setup_stacking=True, name=None):
 
1906
        _client=None, format=None, setup_stacking=True):
2167
1907
        """Create a RemoteBranch instance.
2168
1908
 
2169
1909
        :param real_branch: An optional local implementation of the branch
2175
1915
        :param setup_stacking: If True make an RPC call to determine the
2176
1916
            stacked (or not) status of the branch. If False assume the branch
2177
1917
            is not stacked.
2178
 
        :param name: Colocated branch name
2179
1918
        """
2180
1919
        # We intentionally don't call the parent class's __init__, because it
2181
1920
        # will try to assign to self.tags, which is a property in this subclass.
2182
1921
        # And the parent's __init__ doesn't do much anyway.
 
1922
        self._revision_id_to_revno_cache = None
 
1923
        self._partial_revision_id_to_revno_cache = {}
 
1924
        self._revision_history_cache = None
 
1925
        self._last_revision_info_cache = None
 
1926
        self._merge_sorted_revisions_cache = None
2183
1927
        self.bzrdir = remote_bzrdir
2184
1928
        if _client is not None:
2185
1929
            self._client = _client
2199
1943
        else:
2200
1944
            self._real_branch = None
2201
1945
        # Fill out expected attributes of branch for bzrlib API users.
2202
 
        self._clear_cached_state()
2203
 
        # TODO: deprecate self.base in favor of user_url
2204
 
        self.base = self.bzrdir.user_url
2205
 
        self._name = name
 
1946
        self.base = self.bzrdir.root_transport.base
2206
1947
        self._control_files = None
2207
1948
        self._lock_mode = None
2208
1949
        self._lock_token = None
2219
1960
                    self._real_branch._format.network_name()
2220
1961
        else:
2221
1962
            self._format = format
2222
 
        # when we do _ensure_real we may need to pass ignore_fallbacks to the
2223
 
        # branch.open_branch method.
2224
 
        self._real_ignore_fallbacks = not setup_stacking
2225
1963
        if not self._format._network_name:
2226
1964
            # Did not get from open_branchV2 - old server.
2227
1965
            self._ensure_real()
2232
1970
        hooks = branch.Branch.hooks['open']
2233
1971
        for hook in hooks:
2234
1972
            hook(self)
2235
 
        self._is_stacked = False
2236
1973
        if setup_stacking:
2237
1974
            self._setup_stacking()
2238
1975
 
2244
1981
        except (errors.NotStacked, errors.UnstackableBranchFormat,
2245
1982
            errors.UnstackableRepositoryFormat), e:
2246
1983
            return
2247
 
        self._is_stacked = True
2248
1984
        self._activate_fallback_location(fallback_url)
2249
1985
 
2250
1986
    def _get_config(self):
2272
2008
                raise AssertionError('smart server vfs must be enabled '
2273
2009
                    'to use vfs implementation')
2274
2010
            self.bzrdir._ensure_real()
2275
 
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2276
 
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
 
2011
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
2277
2012
            if self.repository._real_repository is None:
2278
2013
                # Give the remote repository the matching real repo.
2279
2014
                real_repo = self._real_branch.repository
2353
2088
            raise errors.UnexpectedSmartServerResponse(response)
2354
2089
        return response[1]
2355
2090
 
2356
 
    def set_stacked_on_url(self, url):
2357
 
        branch.Branch.set_stacked_on_url(self, url)
2358
 
        if not url:
2359
 
            self._is_stacked = False
2360
 
        else:
2361
 
            self._is_stacked = True
2362
 
        
2363
2091
    def _vfs_get_tags_bytes(self):
2364
2092
        self._ensure_real()
2365
2093
        return self._real_branch._get_tags_bytes()
2375
2103
            return self._vfs_get_tags_bytes()
2376
2104
        return response[0]
2377
2105
 
2378
 
    def _vfs_set_tags_bytes(self, bytes):
2379
 
        self._ensure_real()
2380
 
        return self._real_branch._set_tags_bytes(bytes)
2381
 
 
2382
 
    def _set_tags_bytes(self, bytes):
2383
 
        medium = self._client._medium
2384
 
        if medium._is_remote_before((1, 18)):
2385
 
            self._vfs_set_tags_bytes(bytes)
2386
 
            return
2387
 
        try:
2388
 
            args = (
2389
 
                self._remote_path(), self._lock_token, self._repo_lock_token)
2390
 
            response = self._call_with_body_bytes(
2391
 
                'Branch.set_tags_bytes', args, bytes)
2392
 
        except errors.UnknownSmartMethod:
2393
 
            medium._remember_remote_is_before((1, 18))
2394
 
            self._vfs_set_tags_bytes(bytes)
2395
 
 
2396
2106
    def lock_read(self):
2397
 
        """Lock the branch for read operations.
2398
 
 
2399
 
        :return: An object with an unlock method which will release the lock
2400
 
            obtained.
2401
 
        """
2402
2107
        self.repository.lock_read()
2403
2108
        if not self._lock_mode:
2404
 
            self._note_lock('r')
2405
2109
            self._lock_mode = 'r'
2406
2110
            self._lock_count = 1
2407
2111
            if self._real_branch is not None:
2408
2112
                self._real_branch.lock_read()
2409
2113
        else:
2410
2114
            self._lock_count += 1
2411
 
        return self
2412
2115
 
2413
2116
    def _remote_lock_write(self, token):
2414
2117
        if token is None:
2415
2118
            branch_token = repo_token = ''
2416
2119
        else:
2417
2120
            branch_token = token
2418
 
            repo_token = self.repository.lock_write().repository_token
 
2121
            repo_token = self.repository.lock_write()
2419
2122
            self.repository.unlock()
2420
2123
        err_context = {'token': token}
2421
2124
        response = self._call(
2428
2131
 
2429
2132
    def lock_write(self, token=None):
2430
2133
        if not self._lock_mode:
2431
 
            self._note_lock('w')
2432
2134
            # Lock the branch and repo in one remote call.
2433
2135
            remote_tokens = self._remote_lock_write(token)
2434
2136
            self._lock_token, self._repo_lock_token = remote_tokens
2458
2160
            self._lock_count += 1
2459
2161
            # Re-lock the repository too.
2460
2162
            self.repository.lock_write(self._repo_lock_token)
2461
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2163
        return self._lock_token or None
 
2164
 
 
2165
    def _set_tags_bytes(self, bytes):
 
2166
        self._ensure_real()
 
2167
        return self._real_branch._set_tags_bytes(bytes)
2462
2168
 
2463
2169
    def _unlock(self, branch_token, repo_token):
2464
2170
        err_context = {'token': str((branch_token, repo_token))}
2469
2175
            return
2470
2176
        raise errors.UnexpectedSmartServerResponse(response)
2471
2177
 
2472
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2473
2178
    def unlock(self):
2474
2179
        try:
2475
2180
            self._lock_count -= 1
2515
2220
            raise NotImplementedError(self.dont_leave_lock_in_place)
2516
2221
        self._leave_lock = False
2517
2222
 
2518
 
    @needs_read_lock
2519
 
    def get_rev_id(self, revno, history=None):
2520
 
        if revno == 0:
2521
 
            return _mod_revision.NULL_REVISION
2522
 
        last_revision_info = self.last_revision_info()
2523
 
        ok, result = self.repository.get_rev_id_for_revno(
2524
 
            revno, last_revision_info)
2525
 
        if ok:
2526
 
            return result
2527
 
        missing_parent = result[1]
2528
 
        # Either the revision named by the server is missing, or its parent
2529
 
        # is.  Call get_parent_map to determine which, so that we report a
2530
 
        # useful error.
2531
 
        parent_map = self.repository.get_parent_map([missing_parent])
2532
 
        if missing_parent in parent_map:
2533
 
            missing_parent = parent_map[missing_parent]
2534
 
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2535
 
 
2536
2223
    def _last_revision_info(self):
2537
2224
        response = self._call('Branch.last_revision_info', self._remote_path())
2538
2225
        if response[0] != 'ok':
2543
2230
 
2544
2231
    def _gen_revision_history(self):
2545
2232
        """See Branch._gen_revision_history()."""
2546
 
        if self._is_stacked:
2547
 
            self._ensure_real()
2548
 
            return self._real_branch._gen_revision_history()
2549
2233
        response_tuple, response_handler = self._call_expecting_body(
2550
2234
            'Branch.revision_history', self._remote_path())
2551
2235
        if response_tuple[0] != 'ok':
2886
2570
                    'Missing key %r in context %r', key_err.args[0], context)
2887
2571
                raise err
2888
2572
 
2889
 
    if err.error_verb == 'IncompatibleRepositories':
2890
 
        raise errors.IncompatibleRepositories(err.error_args[0],
2891
 
            err.error_args[1], err.error_args[2])
2892
 
    elif err.error_verb == 'NoSuchRevision':
 
2573
    if err.error_verb == 'NoSuchRevision':
2893
2574
        raise NoSuchRevision(find('branch'), err.error_args[0])
2894
2575
    elif err.error_verb == 'nosuchrevision':
2895
2576
        raise NoSuchRevision(find('repository'), err.error_args[0])
2896
 
    elif err.error_verb == 'nobranch':
2897
 
        if len(err.error_args) >= 1:
2898
 
            extra = err.error_args[0]
2899
 
        else:
2900
 
            extra = None
2901
 
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2902
 
            detail=extra)
 
2577
    elif err.error_tuple == ('nobranch',):
 
2578
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
2903
2579
    elif err.error_verb == 'norepository':
2904
2580
        raise errors.NoRepositoryPresent(find('bzrdir'))
2905
2581
    elif err.error_verb == 'LockContention':