/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: Martin Pool
  • Date: 2008-05-27 03:00:53 UTC
  • mfrom: (3452 +trunk)
  • mto: (3724.1.1 lock-hooks)
  • mto: This revision was merged to the branch mainline in revision 3730.
  • Revision ID: mbp@sourcefrog.net-20080527030053-0mct6dypek0ysjc3
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006, 2007 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
34
34
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
35
35
from bzrlib.config import BranchConfig, TreeConfig
36
36
from bzrlib.decorators import needs_read_lock, needs_write_lock
37
 
from bzrlib.errors import NoSuchRevision
 
37
from bzrlib.errors import (
 
38
    NoSuchRevision,
 
39
    SmartProtocolError,
 
40
    )
38
41
from bzrlib.lockable_files import LockableFiles
39
42
from bzrlib.pack import ContainerPushParser
40
43
from bzrlib.smart import client, vfs
41
44
from bzrlib.symbol_versioning import (
 
45
    deprecated_in,
42
46
    deprecated_method,
43
 
    zero_ninetyone,
44
47
    )
45
 
from bzrlib.revision import NULL_REVISION
 
48
from bzrlib.revision import ensure_null, NULL_REVISION
46
49
from bzrlib.trace import mutter, note, warning
47
50
 
48
51
# Note: RemoteBzrDirFormat is in bzrdir.py
63
66
 
64
67
        if _client is None:
65
68
            medium = transport.get_smart_medium()
66
 
            self._client = client._SmartClient(medium, transport.base)
 
69
            self._client = client._SmartClient(medium)
67
70
        else:
68
71
            self._client = _client
69
72
            return
118
121
    def get_branch_reference(self):
119
122
        """See BzrDir.get_branch_reference()."""
120
123
        path = self._path_for_remote_call(self._client)
121
 
        response = self._client.call('BzrDir.open_branch', path)
 
124
        try:
 
125
            response = self._client.call('BzrDir.open_branch', path)
 
126
        except errors.ErrorFromSmartServer, err:
 
127
            if err.error_tuple == ('nobranch',):
 
128
                raise errors.NotBranchError(path=self.root_transport.base)
 
129
            raise
122
130
        if response[0] == 'ok':
123
131
            if response[1] == '':
124
132
                # branch at this location.
126
134
            else:
127
135
                # a branch reference, use the existing BranchReference logic.
128
136
                return response[1]
129
 
        elif response == ('nobranch',):
130
 
            raise errors.NotBranchError(path=self.root_transport.base)
131
137
        else:
132
138
            raise errors.UnexpectedSmartServerResponse(response)
133
139
 
136
142
        return None, self.open_branch()
137
143
 
138
144
    def open_branch(self, _unsupported=False):
139
 
        assert _unsupported == False, 'unsupported flag support not implemented yet.'
 
145
        if _unsupported:
 
146
            raise NotImplementedError('unsupported flag support not implemented yet.')
140
147
        reference_url = self.get_branch_reference()
141
148
        if reference_url is None:
142
149
            # branch at this location.
149
156
    def open_repository(self):
150
157
        path = self._path_for_remote_call(self._client)
151
158
        verb = 'BzrDir.find_repositoryV2'
152
 
        response = self._client.call(verb, path)
153
 
        if (response == ('error', "Generic bzr smart protocol error: "
154
 
                "bad request '%s'" % verb) or
155
 
              response == ('error', "Generic bzr smart protocol error: "
156
 
                "bad request u'%s'" % verb)):
157
 
            verb = 'BzrDir.find_repository'
158
 
            response = self._client.call(verb, path)
159
 
        assert response[0] in ('ok', 'norepository'), \
160
 
            'unexpected response code %s' % (response,)
161
 
        if response[0] == 'norepository':
162
 
            raise errors.NoRepositoryPresent(self)
 
159
        try:
 
160
            try:
 
161
                response = self._client.call(verb, path)
 
162
            except errors.UnknownSmartMethod:
 
163
                verb = 'BzrDir.find_repository'
 
164
                response = self._client.call(verb, path)
 
165
        except errors.ErrorFromSmartServer, err:
 
166
            if err.error_verb == 'norepository':
 
167
                raise errors.NoRepositoryPresent(self)
 
168
            raise
 
169
        if response[0] != 'ok':
 
170
            raise errors.UnexpectedSmartServerResponse(response)
163
171
        if verb == 'BzrDir.find_repository':
164
172
            # servers that don't support the V2 method don't support external
165
173
            # references either.
166
174
            response = response + ('no', )
167
 
        assert len(response) == 5, 'incorrect response length %s' % (response,)
 
175
        if not (len(response) == 5):
 
176
            raise SmartProtocolError('incorrect response length %s' % (response,))
168
177
        if response[1] == '':
169
178
            format = RemoteRepositoryFormat()
170
179
            format.rich_root_data = (response[2] == 'yes')
228
237
    _matchingbzrdir = RemoteBzrDirFormat
229
238
 
230
239
    def initialize(self, a_bzrdir, shared=False):
231
 
        assert isinstance(a_bzrdir, RemoteBzrDir), \
232
 
            '%r is not a RemoteBzrDir' % (a_bzrdir,)
 
240
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
241
            raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
233
242
        return a_bzrdir.create_repository(shared=shared)
234
243
    
235
244
    def open(self, a_bzrdir):
236
 
        assert isinstance(a_bzrdir, RemoteBzrDir)
 
245
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
246
            raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
237
247
        return a_bzrdir.open_repository()
238
248
 
239
249
    def get_format_description(self):
373
383
            return {}
374
384
 
375
385
        path = self.bzrdir._path_for_remote_call(self._client)
376
 
        assert type(revision_id) is str
377
 
        response = self._client.call_expecting_body(
378
 
            'Repository.get_revision_graph', path, revision_id)
379
 
        if response[0][0] not in ['ok', 'nosuchrevision']:
380
 
            raise errors.UnexpectedSmartServerResponse(response[0])
381
 
        if response[0][0] == 'ok':
382
 
            coded = response[1].read_body_bytes()
383
 
            if coded == '':
384
 
                # no revisions in this repository!
385
 
                return {}
386
 
            lines = coded.split('\n')
387
 
            revision_graph = {}
388
 
            for line in lines:
389
 
                d = tuple(line.split())
390
 
                revision_graph[d[0]] = d[1:]
391
 
                
392
 
            return revision_graph
393
 
        else:
394
 
            response_body = response[1].read_body_bytes()
395
 
            assert response_body == ''
396
 
            raise NoSuchRevision(self, revision_id)
 
386
        try:
 
387
            response = self._client.call_expecting_body(
 
388
                'Repository.get_revision_graph', path, revision_id)
 
389
        except errors.ErrorFromSmartServer, err:
 
390
            if err.error_verb == 'nosuchrevision':
 
391
                raise NoSuchRevision(self, revision_id)
 
392
            raise
 
393
        response_tuple, response_handler = response
 
394
        if response_tuple[0] != 'ok':
 
395
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
396
        coded = response_handler.read_body_bytes()
 
397
        if coded == '':
 
398
            # no revisions in this repository!
 
399
            return {}
 
400
        lines = coded.split('\n')
 
401
        revision_graph = {}
 
402
        for line in lines:
 
403
            d = tuple(line.split())
 
404
            revision_graph[d[0]] = d[1:]
 
405
            
 
406
        return revision_graph
397
407
 
398
408
    def has_revision(self, revision_id):
399
409
        """See Repository.has_revision()."""
401
411
            # The null revision is always present.
402
412
            return True
403
413
        path = self.bzrdir._path_for_remote_call(self._client)
404
 
        response = self._client.call('Repository.has_revision', path, revision_id)
405
 
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
 
414
        response = self._client.call(
 
415
            'Repository.has_revision', path, revision_id)
 
416
        if response[0] not in ('yes', 'no'):
 
417
            raise errors.UnexpectedSmartServerResponse(response)
406
418
        return response[0] == 'yes'
407
419
 
408
420
    def has_revisions(self, revision_ids):
439
451
            fmt_committers = 'no'
440
452
        else:
441
453
            fmt_committers = 'yes'
442
 
        response = self._client.call_expecting_body(
 
454
        response_tuple, response_handler = self._client.call_expecting_body(
443
455
            'Repository.gather_stats', path, fmt_revid, fmt_committers)
444
 
        assert response[0][0] == 'ok', \
445
 
            'unexpected response code %s' % (response[0],)
 
456
        if response_tuple[0] != 'ok':
 
457
            raise errors.UnexpectedSmartServerResponse(response_tuple)
446
458
 
447
 
        body = response[1].read_body_bytes()
 
459
        body = response_handler.read_body_bytes()
448
460
        result = {}
449
461
        for line in body.split('\n'):
450
462
            if not line:
485
497
        """See Repository.is_shared()."""
486
498
        path = self.bzrdir._path_for_remote_call(self._client)
487
499
        response = self._client.call('Repository.is_shared', path)
488
 
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
 
500
        if response[0] not in ('yes', 'no'):
 
501
            raise SmartProtocolError('unexpected response code %s' % (response,))
489
502
        return response[0] == 'yes'
490
503
 
491
504
    def is_write_locked(self):
508
521
        path = self.bzrdir._path_for_remote_call(self._client)
509
522
        if token is None:
510
523
            token = ''
511
 
        response = self._client.call('Repository.lock_write', path, token)
 
524
        try:
 
525
            response = self._client.call('Repository.lock_write', path, token)
 
526
        except errors.ErrorFromSmartServer, err:
 
527
            if err.error_verb == 'LockContention':
 
528
                raise errors.LockContention('(remote lock)')
 
529
            elif err.error_verb == 'UnlockableTransport':
 
530
                raise errors.UnlockableTransport(self.bzrdir.root_transport)
 
531
            elif err.error_verb == 'LockFailed':
 
532
                raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
533
            raise
 
534
 
512
535
        if response[0] == 'ok':
513
536
            ok, token = response
514
537
            return token
515
 
        elif response[0] == 'LockContention':
516
 
            raise errors.LockContention('(remote lock)')
517
 
        elif response[0] == 'UnlockableTransport':
518
 
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
519
 
        elif response[0] == 'LockFailed':
520
 
            raise errors.LockFailed(response[1], response[2])
521
538
        else:
522
539
            raise errors.UnexpectedSmartServerResponse(response)
523
540
 
561
578
        :param repository: The repository to fallback to for non-hpss
562
579
            implemented operations.
563
580
        """
564
 
        assert not isinstance(repository, RemoteRepository)
 
581
        if isinstance(repository, RemoteRepository):
 
582
            raise AssertionError()
565
583
        self._real_repository = repository
566
584
        if self._lock_mode == 'w':
567
585
            # if we are already locked, the real repository must be able to
586
604
        if not token:
587
605
            # with no token the remote repository is not persistently locked.
588
606
            return
589
 
        response = self._client.call('Repository.unlock', path, token)
 
607
        try:
 
608
            response = self._client.call('Repository.unlock', path, token)
 
609
        except errors.ErrorFromSmartServer, err:
 
610
            if err.error_verb == 'TokenMismatch':
 
611
                raise errors.TokenMismatch(token, '(remote token)')
 
612
            raise
590
613
        if response == ('ok',):
591
614
            return
592
 
        elif response[0] == 'TokenMismatch':
593
 
            raise errors.TokenMismatch(token, '(remote token)')
594
615
        else:
595
616
            raise errors.UnexpectedSmartServerResponse(response)
596
617
 
634
655
        """
635
656
        import tempfile
636
657
        path = self.bzrdir._path_for_remote_call(self._client)
637
 
        response, protocol = self._client.call_expecting_body(
638
 
            'Repository.tarball', path, compression)
 
658
        try:
 
659
            response, protocol = self._client.call_expecting_body(
 
660
                'Repository.tarball', path, compression)
 
661
        except errors.UnknownSmartMethod:
 
662
            protocol.cancel_read_body()
 
663
            return None
639
664
        if response[0] == 'ok':
640
665
            # Extract the tarball and return it
641
666
            t = tempfile.NamedTemporaryFile()
643
668
            t.write(protocol.read_body_bytes())
644
669
            t.seek(0)
645
670
            return t
646
 
        if (response == ('error', "Generic bzr smart protocol error: "
647
 
                "bad request 'Repository.tarball'") or
648
 
              response == ('error', "Generic bzr smart protocol error: "
649
 
                "bad request u'Repository.tarball'")):
650
 
            protocol.cancel_read_body()
651
 
            return None
652
671
        raise errors.UnexpectedSmartServerResponse(response)
653
672
 
654
673
    def sprout(self, to_bzrdir, revision_id=None):
718
737
        return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
719
738
 
720
739
    def make_working_trees(self):
721
 
        """RemoteRepositories never create working trees by default."""
722
 
        return False
 
740
        """See Repository.make_working_trees"""
 
741
        self._ensure_real()
 
742
        return self._real_repository.make_working_trees()
723
743
 
724
744
    def revision_ids_to_search_result(self, result_set):
725
745
        """Convert a set of revision ids to a graph SearchResult."""
810
830
            ancestry.update(parent_map)
811
831
        present_keys = [k for k in keys if k in ancestry]
812
832
        if 'hpss' in debug.debug_flags:
813
 
            self._requested_parents.update(present_keys)
814
 
            mutter('Current RemoteRepository graph hit rate: %d%%',
815
 
                100.0 * len(self._requested_parents) / len(ancestry))
 
833
            if self._requested_parents is not None and len(ancestry) != 0:
 
834
                self._requested_parents.update(present_keys)
 
835
                mutter('Current RemoteRepository graph hit rate: %d%%',
 
836
                    100.0 * len(self._requested_parents) / len(ancestry))
816
837
        return dict((k, ancestry[k]) for k in present_keys)
817
838
 
818
 
    def _response_is_unknown_method(self, response, verb):
819
 
        """Return True if response is an unknonwn method response to verb.
820
 
        
821
 
        :param response: The response from a smart client call_expecting_body
822
 
            call.
823
 
        :param verb: The verb used in that call.
824
 
        :return: True if an unknown method was encountered.
825
 
        """
826
 
        # This might live better on
827
 
        # bzrlib.smart.protocol.SmartClientRequestProtocolOne
828
 
        if (response[0] == ('error', "Generic bzr smart protocol error: "
829
 
                "bad request '%s'" % verb) or
830
 
              response[0] == ('error', "Generic bzr smart protocol error: "
831
 
                "bad request u'%s'" % verb)):
832
 
           response[1].cancel_read_body()
833
 
           return True
834
 
        return False
835
 
 
836
839
    def _get_parent_map(self, keys):
837
840
        """Helper for get_parent_map that performs the RPC."""
838
841
        medium = self._client._medium
844
847
            # :- its because we're working with a deprecated server anyway, and
845
848
            # the user will almost certainly have seen a warning about the
846
849
            # server version already.
847
 
            return self.get_revision_graph()
 
850
            rg = self.get_revision_graph()
 
851
            # There is an api discrepency between get_parent_map and
 
852
            # get_revision_graph. Specifically, a "key:()" pair in
 
853
            # get_revision_graph just means a node has no parents. For
 
854
            # "get_parent_map" it means the node is a ghost. So fix up the
 
855
            # graph to correct this.
 
856
            #   https://bugs.launchpad.net/bzr/+bug/214894
 
857
            # There is one other "bug" which is that ghosts in
 
858
            # get_revision_graph() are not returned at all. But we won't worry
 
859
            # about that for now.
 
860
            for node_id, parent_ids in rg.iteritems():
 
861
                if parent_ids == ():
 
862
                    rg[node_id] = (NULL_REVISION,)
 
863
            rg[NULL_REVISION] = ()
 
864
            return rg
848
865
 
849
866
        keys = set(keys)
 
867
        if None in keys:
 
868
            raise ValueError('get_parent_map(None) is not valid')
850
869
        if NULL_REVISION in keys:
851
870
            keys.discard(NULL_REVISION)
852
871
            found_parents = {NULL_REVISION:()}
880
899
        body = self._serialise_search_recipe(recipe)
881
900
        path = self.bzrdir._path_for_remote_call(self._client)
882
901
        for key in keys:
883
 
            assert type(key) is str
 
902
            if type(key) is not str:
 
903
                raise ValueError(
 
904
                    "key %r not a plain string" % (key,))
884
905
        verb = 'Repository.get_parent_map'
885
906
        args = (path,) + tuple(keys)
886
 
        response = self._client.call_with_body_bytes_expecting_body(
887
 
            verb, args, self._serialise_search_recipe(recipe))
888
 
        if self._response_is_unknown_method(response, verb):
 
907
        try:
 
908
            response = self._client.call_with_body_bytes_expecting_body(
 
909
                verb, args, self._serialise_search_recipe(recipe))
 
910
        except errors.UnknownSmartMethod:
889
911
            # Server does not support this method, so get the whole graph.
890
912
            # Worse, we have to force a disconnection, because the server now
891
913
            # doesn't realise it has a body on the wire to consume, so the
897
919
            # To avoid having to disconnect repeatedly, we keep track of the
898
920
            # fact the server doesn't understand remote methods added in 1.2.
899
921
            medium._remote_is_at_least_1_2 = False
900
 
            return self._get_revision_graph(None)
901
 
        elif response[0][0] not in ['ok']:
902
 
            reponse[1].cancel_read_body()
903
 
            raise errors.UnexpectedSmartServerResponse(response[0])
904
 
        if response[0][0] == 'ok':
905
 
            coded = bz2.decompress(response[1].read_body_bytes())
 
922
            return self.get_revision_graph(None)
 
923
        response_tuple, response_handler = response
 
924
        if response_tuple[0] not in ['ok']:
 
925
            response_handler.cancel_read_body()
 
926
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
927
        if response_tuple[0] == 'ok':
 
928
            coded = bz2.decompress(response_handler.read_body_bytes())
906
929
            if coded == '':
907
930
                # no revisions found
908
931
                return {}
1015
1038
        return self._real_repository.pack()
1016
1039
 
1017
1040
    def set_make_working_trees(self, new_value):
1018
 
        raise NotImplementedError(self.set_make_working_trees)
 
1041
        self._ensure_real()
 
1042
        self._real_repository.set_make_working_trees(new_value)
1019
1043
 
1020
1044
    @needs_write_lock
1021
1045
    def sign_revision(self, revision_id, gpg_strategy):
1061
1085
        REQUEST_NAME = 'Repository.stream_revisions_chunked'
1062
1086
        path = self.bzrdir._path_for_remote_call(self._client)
1063
1087
        body = self._serialise_search_recipe(search.get_recipe())
1064
 
        response, protocol = self._client.call_with_body_bytes_expecting_body(
1065
 
            REQUEST_NAME, (path,), body)
1066
 
 
1067
 
        if self._response_is_unknown_method((response, protocol), REQUEST_NAME):
 
1088
        try:
 
1089
            result = self._client.call_with_body_bytes_expecting_body(
 
1090
                REQUEST_NAME, (path,), body)
 
1091
            response, protocol = result
 
1092
        except errors.UnknownSmartMethod:
1068
1093
            # Server does not support this method, so fall back to VFS.
1069
1094
            # Worse, we have to force a disconnection, because the server now
1070
1095
            # doesn't realise it has a body on the wire to consume, so the
1160
1185
        self._dir_mode = None
1161
1186
        self._file_mode = None
1162
1187
 
1163
 
    def get(self, path):
1164
 
        """'get' a remote path as per the LockableFiles interface.
1165
 
 
1166
 
        :param path: the file to 'get'. If this is 'branch.conf', we do not
1167
 
             just retrieve a file, instead we ask the smart server to generate
1168
 
             a configuration for us - which is retrieved as an INI file.
1169
 
        """
1170
 
        if path == 'branch.conf':
1171
 
            path = self.bzrdir._path_for_remote_call(self._client)
1172
 
            response = self._client.call_expecting_body(
1173
 
                'Branch.get_config_file', path)
1174
 
            assert response[0][0] == 'ok', \
1175
 
                'unexpected response code %s' % (response[0],)
1176
 
            return StringIO(response[1].read_body_bytes())
1177
 
        else:
1178
 
            # VFS fallback.
1179
 
            return LockableFiles.get(self, path)
1180
 
 
1181
1188
 
1182
1189
class RemoteBranchFormat(branch.BranchFormat):
1183
1190
 
1192
1199
        return 'Remote BZR Branch'
1193
1200
 
1194
1201
    def open(self, a_bzrdir):
1195
 
        assert isinstance(a_bzrdir, RemoteBzrDir)
1196
1202
        return a_bzrdir.open_branch()
1197
1203
 
1198
1204
    def initialize(self, a_bzrdir):
1199
 
        assert isinstance(a_bzrdir, RemoteBzrDir)
1200
1205
        return a_bzrdir.create_branch()
1201
1206
 
1202
1207
    def supports_tags(self):
1248
1253
        self._control_files = None
1249
1254
        self._lock_mode = None
1250
1255
        self._lock_token = None
 
1256
        self._repo_lock_token = None
1251
1257
        self._lock_count = 0
1252
1258
        self._leave_lock = False
1253
1259
 
 
1260
    def _ensure_real_transport(self):
 
1261
        # if we try vfs access, return the real branch's vfs transport
 
1262
        self._ensure_real()
 
1263
        return self._real_branch._transport
 
1264
 
 
1265
    _transport = property(_ensure_real_transport)
 
1266
 
1254
1267
    def __str__(self):
1255
1268
        return "%s(%s)" % (self.__class__.__name__, self.base)
1256
1269
 
1261
1274
 
1262
1275
        Used before calls to self._real_branch.
1263
1276
        """
1264
 
        if not self._real_branch:
1265
 
            assert vfs.vfs_enabled()
 
1277
        if self._real_branch is None:
 
1278
            if not vfs.vfs_enabled():
 
1279
                raise AssertionError('smart server vfs must be enabled '
 
1280
                    'to use vfs implementation')
1266
1281
            self.bzrdir._ensure_real()
1267
1282
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
1268
1283
            # Give the remote repository the matching real repo.
1313
1328
            repo_token = self.repository.lock_write()
1314
1329
            self.repository.unlock()
1315
1330
        path = self.bzrdir._path_for_remote_call(self._client)
1316
 
        response = self._client.call('Branch.lock_write', path, branch_token,
1317
 
                                     repo_token or '')
1318
 
        if response[0] == 'ok':
1319
 
            ok, branch_token, repo_token = response
1320
 
            return branch_token, repo_token
1321
 
        elif response[0] == 'LockContention':
1322
 
            raise errors.LockContention('(remote lock)')
1323
 
        elif response[0] == 'TokenMismatch':
1324
 
            raise errors.TokenMismatch(token, '(remote token)')
1325
 
        elif response[0] == 'UnlockableTransport':
1326
 
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
1327
 
        elif response[0] == 'ReadOnlyError':
1328
 
            raise errors.ReadOnlyError(self)
1329
 
        elif response[0] == 'LockFailed':
1330
 
            raise errors.LockFailed(response[1], response[2])
1331
 
        else:
 
1331
        try:
 
1332
            response = self._client.call(
 
1333
                'Branch.lock_write', path, branch_token, repo_token or '')
 
1334
        except errors.ErrorFromSmartServer, err:
 
1335
            if err.error_verb == 'LockContention':
 
1336
                raise errors.LockContention('(remote lock)')
 
1337
            elif err.error_verb == 'TokenMismatch':
 
1338
                raise errors.TokenMismatch(token, '(remote token)')
 
1339
            elif err.error_verb == 'UnlockableTransport':
 
1340
                raise errors.UnlockableTransport(self.bzrdir.root_transport)
 
1341
            elif err.error_verb == 'ReadOnlyError':
 
1342
                raise errors.ReadOnlyError(self)
 
1343
            elif err.error_verb == 'LockFailed':
 
1344
                raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
1345
            raise
 
1346
        if response[0] != 'ok':
1332
1347
            raise errors.UnexpectedSmartServerResponse(response)
 
1348
        ok, branch_token, repo_token = response
 
1349
        return branch_token, repo_token
1333
1350
            
1334
1351
    def lock_write(self, token=None):
1335
1352
        if not self._lock_mode:
1336
1353
            remote_tokens = self._remote_lock_write(token)
1337
1354
            self._lock_token, self._repo_lock_token = remote_tokens
1338
 
            assert self._lock_token, 'Remote server did not return a token!'
 
1355
            if not self._lock_token:
 
1356
                raise SmartProtocolError('Remote server did not return a token!')
1339
1357
            # TODO: We really, really, really don't want to call _ensure_real
1340
1358
            # here, but it's the easiest way to ensure coherency between the
1341
1359
            # state of the RemoteBranch and RemoteRepository objects and the
1371
1389
 
1372
1390
    def _unlock(self, branch_token, repo_token):
1373
1391
        path = self.bzrdir._path_for_remote_call(self._client)
1374
 
        response = self._client.call('Branch.unlock', path, branch_token,
1375
 
                                     repo_token or '')
 
1392
        try:
 
1393
            response = self._client.call('Branch.unlock', path, branch_token,
 
1394
                                         repo_token or '')
 
1395
        except errors.ErrorFromSmartServer, err:
 
1396
            if err.error_verb == 'TokenMismatch':
 
1397
                raise errors.TokenMismatch(
 
1398
                    str((branch_token, repo_token)), '(remote tokens)')
 
1399
            raise
1376
1400
        if response == ('ok',):
1377
1401
            return
1378
 
        elif response[0] == 'TokenMismatch':
1379
 
            raise errors.TokenMismatch(
1380
 
                str((branch_token, repo_token)), '(remote tokens)')
1381
 
        else:
1382
 
            raise errors.UnexpectedSmartServerResponse(response)
 
1402
        raise errors.UnexpectedSmartServerResponse(response)
1383
1403
 
1384
1404
    def unlock(self):
1385
1405
        self._lock_count -= 1
1400
1420
                # Only write-locked branched need to make a remote method call
1401
1421
                # to perfom the unlock.
1402
1422
                return
1403
 
            assert self._lock_token, 'Locked, but no token!'
 
1423
            if not self._lock_token:
 
1424
                raise AssertionError('Locked, but no token!')
1404
1425
            branch_token = self._lock_token
1405
1426
            repo_token = self._repo_lock_token
1406
1427
            self._lock_token = None
1426
1447
        """See Branch.last_revision_info()."""
1427
1448
        path = self.bzrdir._path_for_remote_call(self._client)
1428
1449
        response = self._client.call('Branch.last_revision_info', path)
1429
 
        assert response[0] == 'ok', 'unexpected response code %s' % (response,)
 
1450
        if response[0] != 'ok':
 
1451
            raise SmartProtocolError('unexpected response code %s' % (response,))
1430
1452
        revno = int(response[1])
1431
1453
        last_revision = response[2]
1432
1454
        return (revno, last_revision)
1434
1456
    def _gen_revision_history(self):
1435
1457
        """See Branch._gen_revision_history()."""
1436
1458
        path = self.bzrdir._path_for_remote_call(self._client)
1437
 
        response = self._client.call_expecting_body(
 
1459
        response_tuple, response_handler = self._client.call_expecting_body(
1438
1460
            'Branch.revision_history', path)
1439
 
        assert response[0][0] == 'ok', ('unexpected response code %s'
1440
 
                                        % (response[0],))
1441
 
        result = response[1].read_body_bytes().split('\x00')
 
1461
        if response_tuple[0] != 'ok':
 
1462
            raise UnexpectedSmartServerResponse(response_tuple)
 
1463
        result = response_handler.read_body_bytes().split('\x00')
1442
1464
        if result == ['']:
1443
1465
            return []
1444
1466
        return result
1454
1476
        else:
1455
1477
            rev_id = rev_history[-1]
1456
1478
        self._clear_cached_state()
1457
 
        response = self._client.call('Branch.set_last_revision',
1458
 
            path, self._lock_token, self._repo_lock_token, rev_id)
1459
 
        if response[0] == 'NoSuchRevision':
1460
 
            raise NoSuchRevision(self, rev_id)
1461
 
        else:
1462
 
            assert response == ('ok',), (
1463
 
                'unexpected response code %r' % (response,))
 
1479
        try:
 
1480
            response = self._client.call('Branch.set_last_revision',
 
1481
                path, self._lock_token, self._repo_lock_token, rev_id)
 
1482
        except errors.ErrorFromSmartServer, err:
 
1483
            if err.error_verb == 'NoSuchRevision':
 
1484
                raise NoSuchRevision(self, rev_id)
 
1485
            raise
 
1486
        if response != ('ok',):
 
1487
            raise errors.UnexpectedSmartServerResponse(response)
1464
1488
        self._cache_revision_history(rev_history)
1465
1489
 
1466
1490
    def get_parent(self):
1471
1495
        self._ensure_real()
1472
1496
        return self._real_branch.set_parent(url)
1473
1497
        
1474
 
    def get_config(self):
1475
 
        return RemoteBranchConfig(self)
1476
 
 
1477
1498
    def sprout(self, to_bzrdir, revision_id=None):
1478
1499
        # Like Branch.sprout, except that it sprouts a branch in the default
1479
1500
        # format, because RemoteBranches can't be created at arbitrary URLs.
1508
1529
    def is_locked(self):
1509
1530
        return self._lock_count >= 1
1510
1531
 
 
1532
    @needs_write_lock
1511
1533
    def set_last_revision_info(self, revno, revision_id):
1512
 
        self._ensure_real()
1513
 
        self._clear_cached_state()
1514
 
        return self._real_branch.set_last_revision_info(revno, revision_id)
 
1534
        revision_id = ensure_null(revision_id)
 
1535
        path = self.bzrdir._path_for_remote_call(self._client)
 
1536
        try:
 
1537
            response = self._client.call('Branch.set_last_revision_info',
 
1538
                path, self._lock_token, self._repo_lock_token, str(revno), revision_id)
 
1539
        except errors.UnknownSmartMethod:
 
1540
            self._ensure_real()
 
1541
            self._clear_cached_state()
 
1542
            return self._real_branch.set_last_revision_info(revno, revision_id)
 
1543
        except errors.ErrorFromSmartServer, err:
 
1544
            if err.error_verb == 'NoSuchRevision':
 
1545
                raise NoSuchRevision(self, err.error_args[0])
 
1546
            raise
 
1547
        if response == ('ok',):
 
1548
            self._clear_cached_state()
 
1549
        else:
 
1550
            raise errors.UnexpectedSmartServerResponse(response)
1515
1551
 
1516
1552
    def generate_revision_history(self, revision_id, last_rev=None,
1517
1553
                                  other_branch=None):
1534
1570
            other, stop_revision=stop_revision, overwrite=overwrite)
1535
1571
 
1536
1572
 
1537
 
class RemoteBranchConfig(BranchConfig):
1538
 
 
1539
 
    def username(self):
1540
 
        self.branch._ensure_real()
1541
 
        return self.branch._real_branch.get_config().username()
1542
 
 
1543
 
    def _get_branch_data_config(self):
1544
 
        self.branch._ensure_real()
1545
 
        if self._branch_data_config is None:
1546
 
            self._branch_data_config = TreeConfig(self.branch._real_branch)
1547
 
        return self._branch_data_config
1548
 
 
1549
 
 
1550
1573
def _extract_tar(tar, to_dir):
1551
1574
    """Extract all the contents of a tarfile object.
1552
1575