~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

Implement version 3 of the network protocol. (Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
from bzrlib.lockable_files import LockableFiles
42
42
from bzrlib.pack import ContainerPushParser
43
43
from bzrlib.smart import client, vfs
44
 
from bzrlib.symbol_versioning import (
45
 
    deprecated_method,
46
 
    )
47
44
from bzrlib.revision import ensure_null, NULL_REVISION
48
45
from bzrlib.trace import mutter, note, warning
49
46
 
120
117
    def get_branch_reference(self):
121
118
        """See BzrDir.get_branch_reference()."""
122
119
        path = self._path_for_remote_call(self._client)
123
 
        response = self._client.call('BzrDir.open_branch', path)
 
120
        try:
 
121
            response = self._client.call('BzrDir.open_branch', path)
 
122
        except errors.ErrorFromSmartServer, err:
 
123
            if err.error_tuple == ('nobranch',):
 
124
                raise errors.NotBranchError(path=self.root_transport.base)
 
125
            raise
124
126
        if response[0] == 'ok':
125
127
            if response[1] == '':
126
128
                # branch at this location.
128
130
            else:
129
131
                # a branch reference, use the existing BranchReference logic.
130
132
                return response[1]
131
 
        elif response == ('nobranch',):
132
 
            raise errors.NotBranchError(path=self.root_transport.base)
133
133
        else:
134
134
            raise errors.UnexpectedSmartServerResponse(response)
135
135
 
153
153
        path = self._path_for_remote_call(self._client)
154
154
        verb = 'BzrDir.find_repositoryV2'
155
155
        try:
156
 
            response = self._client.call(verb, path)
157
 
        except errors.UnknownSmartMethod:
158
 
            verb = 'BzrDir.find_repository'
159
 
            response = self._client.call(verb, path)
160
 
        if response[0] == 'norepository':
161
 
            raise errors.NoRepositoryPresent(self)
162
 
        elif response[0] != 'ok':
163
 
            raise SmartProtocolError('unexpected response %r' 
164
 
                % response)
 
156
            try:
 
157
                response = self._client.call(verb, path)
 
158
            except errors.UnknownSmartMethod:
 
159
                verb = 'BzrDir.find_repository'
 
160
                response = self._client.call(verb, path)
 
161
        except errors.ErrorFromSmartServer, err:
 
162
            if err.error_verb == 'norepository':
 
163
                raise errors.NoRepositoryPresent(self)
 
164
            raise
 
165
        if response[0] != 'ok':
 
166
            raise errors.UnexpectedSmartServerResponse(response)
165
167
        if verb == 'BzrDir.find_repository':
166
168
            # servers that don't support the V2 method don't support external
167
169
            # references either.
377
379
            return {}
378
380
 
379
381
        path = self.bzrdir._path_for_remote_call(self._client)
380
 
        response = self._client.call_expecting_body(
381
 
            'Repository.get_revision_graph', path, revision_id)
382
 
        if response[0][0] not in ['ok', 'nosuchrevision']:
383
 
            raise errors.UnexpectedSmartServerResponse(response[0])
384
 
        if response[0][0] == 'ok':
385
 
            coded = response[1].read_body_bytes()
386
 
            if coded == '':
387
 
                # no revisions in this repository!
388
 
                return {}
389
 
            lines = coded.split('\n')
390
 
            revision_graph = {}
391
 
            for line in lines:
392
 
                d = tuple(line.split())
393
 
                revision_graph[d[0]] = d[1:]
394
 
                
395
 
            return revision_graph
396
 
        else:
397
 
            response_body = response[1].read_body_bytes()
398
 
            if response_body:
399
 
                raise SmartProtocolError('unexpected response body')
400
 
            raise NoSuchRevision(self, revision_id)
 
382
        try:
 
383
            response = self._client.call_expecting_body(
 
384
                'Repository.get_revision_graph', path, revision_id)
 
385
        except errors.ErrorFromSmartServer, err:
 
386
            if err.error_verb == 'nosuchrevision':
 
387
                raise NoSuchRevision(self, revision_id)
 
388
            raise
 
389
        response_tuple, response_handler = response
 
390
        if response_tuple[0] != 'ok':
 
391
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
392
        coded = response_handler.read_body_bytes()
 
393
        if coded == '':
 
394
            # no revisions in this repository!
 
395
            return {}
 
396
        lines = coded.split('\n')
 
397
        revision_graph = {}
 
398
        for line in lines:
 
399
            d = tuple(line.split())
 
400
            revision_graph[d[0]] = d[1:]
 
401
            
 
402
        return revision_graph
401
403
 
402
404
    def has_revision(self, revision_id):
403
405
        """See Repository.has_revision()."""
405
407
            # The null revision is always present.
406
408
            return True
407
409
        path = self.bzrdir._path_for_remote_call(self._client)
408
 
        response = self._client.call('Repository.has_revision', path, revision_id)
 
410
        response = self._client.call(
 
411
            'Repository.has_revision', path, revision_id)
409
412
        if response[0] not in ('yes', 'no'):
410
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
413
            raise errors.UnexpectedSmartServerResponse(response)
411
414
        return response[0] == 'yes'
412
415
 
413
416
    def has_revisions(self, revision_ids):
444
447
            fmt_committers = 'no'
445
448
        else:
446
449
            fmt_committers = 'yes'
447
 
        response = self._client.call_expecting_body(
 
450
        response_tuple, response_handler = self._client.call_expecting_body(
448
451
            'Repository.gather_stats', path, fmt_revid, fmt_committers)
449
 
        if response[0][0] != 'ok':
450
 
            raise SmartProtocolError('unexpected response code %s'
451
 
                % (response[0],))
 
452
        if response_tuple[0] != 'ok':
 
453
            raise errors.UnexpectedSmartServerResponse(response_tuple)
452
454
 
453
 
        body = response[1].read_body_bytes()
 
455
        body = response_handler.read_body_bytes()
454
456
        result = {}
455
457
        for line in body.split('\n'):
456
458
            if not line:
515
517
        path = self.bzrdir._path_for_remote_call(self._client)
516
518
        if token is None:
517
519
            token = ''
518
 
        response = self._client.call('Repository.lock_write', path, token)
 
520
        try:
 
521
            response = self._client.call('Repository.lock_write', path, token)
 
522
        except errors.ErrorFromSmartServer, err:
 
523
            if err.error_verb == 'LockContention':
 
524
                raise errors.LockContention('(remote lock)')
 
525
            elif err.error_verb == 'UnlockableTransport':
 
526
                raise errors.UnlockableTransport(self.bzrdir.root_transport)
 
527
            elif err.error_verb == 'LockFailed':
 
528
                raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
529
            raise
 
530
 
519
531
        if response[0] == 'ok':
520
532
            ok, token = response
521
533
            return token
522
 
        elif response[0] == 'LockContention':
523
 
            raise errors.LockContention('(remote lock)')
524
 
        elif response[0] == 'UnlockableTransport':
525
 
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
526
 
        elif response[0] == 'LockFailed':
527
 
            raise errors.LockFailed(response[1], response[2])
528
534
        else:
529
535
            raise errors.UnexpectedSmartServerResponse(response)
530
536
 
594
600
        if not token:
595
601
            # with no token the remote repository is not persistently locked.
596
602
            return
597
 
        response = self._client.call('Repository.unlock', path, token)
 
603
        try:
 
604
            response = self._client.call('Repository.unlock', path, token)
 
605
        except errors.ErrorFromSmartServer, err:
 
606
            if err.error_verb == 'TokenMismatch':
 
607
                raise errors.TokenMismatch(token, '(remote token)')
 
608
            raise
598
609
        if response == ('ok',):
599
610
            return
600
 
        elif response[0] == 'TokenMismatch':
601
 
            raise errors.TokenMismatch(token, '(remote token)')
602
611
        else:
603
612
            raise errors.UnexpectedSmartServerResponse(response)
604
613
 
817
826
            ancestry.update(parent_map)
818
827
        present_keys = [k for k in keys if k in ancestry]
819
828
        if 'hpss' in debug.debug_flags:
820
 
            self._requested_parents.update(present_keys)
821
 
            mutter('Current RemoteRepository graph hit rate: %d%%',
822
 
                100.0 * len(self._requested_parents) / len(ancestry))
 
829
            if self._requested_parents is not None and len(ancestry) != 0:
 
830
                self._requested_parents.update(present_keys)
 
831
                mutter('Current RemoteRepository graph hit rate: %d%%',
 
832
                    100.0 * len(self._requested_parents) / len(ancestry))
823
833
        return dict((k, ancestry[k]) for k in present_keys)
824
834
 
825
835
    def _get_parent_map(self, keys):
904
914
            # fact the server doesn't understand remote methods added in 1.2.
905
915
            medium._remote_is_at_least_1_2 = False
906
916
            return self.get_revision_graph(None)
907
 
        if response[0][0] not in ['ok']:
908
 
            response[1].cancel_read_body()
909
 
            raise errors.UnexpectedSmartServerResponse(response[0])
910
 
        if response[0][0] == 'ok':
911
 
            coded = bz2.decompress(response[1].read_body_bytes())
 
917
        response_tuple, response_handler = response
 
918
        if response_tuple[0] not in ['ok']:
 
919
            response_handler.cancel_read_body()
 
920
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
921
        if response_tuple[0] == 'ok':
 
922
            coded = bz2.decompress(response_handler.read_body_bytes())
912
923
            if coded == '':
913
924
                # no revisions found
914
925
                return {}
1304
1315
            repo_token = self.repository.lock_write()
1305
1316
            self.repository.unlock()
1306
1317
        path = self.bzrdir._path_for_remote_call(self._client)
1307
 
        response = self._client.call('Branch.lock_write', path, branch_token,
1308
 
                                     repo_token or '')
1309
 
        if response[0] == 'ok':
1310
 
            ok, branch_token, repo_token = response
1311
 
            return branch_token, repo_token
1312
 
        elif response[0] == 'LockContention':
1313
 
            raise errors.LockContention('(remote lock)')
1314
 
        elif response[0] == 'TokenMismatch':
1315
 
            raise errors.TokenMismatch(token, '(remote token)')
1316
 
        elif response[0] == 'UnlockableTransport':
1317
 
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
1318
 
        elif response[0] == 'ReadOnlyError':
1319
 
            raise errors.ReadOnlyError(self)
1320
 
        elif response[0] == 'LockFailed':
1321
 
            raise errors.LockFailed(response[1], response[2])
1322
 
        else:
 
1318
        try:
 
1319
            response = self._client.call(
 
1320
                'Branch.lock_write', path, branch_token, repo_token or '')
 
1321
        except errors.ErrorFromSmartServer, err:
 
1322
            if err.error_verb == 'LockContention':
 
1323
                raise errors.LockContention('(remote lock)')
 
1324
            elif err.error_verb == 'TokenMismatch':
 
1325
                raise errors.TokenMismatch(token, '(remote token)')
 
1326
            elif err.error_verb == 'UnlockableTransport':
 
1327
                raise errors.UnlockableTransport(self.bzrdir.root_transport)
 
1328
            elif err.error_verb == 'ReadOnlyError':
 
1329
                raise errors.ReadOnlyError(self)
 
1330
            elif err.error_verb == 'LockFailed':
 
1331
                raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
1332
            raise
 
1333
        if response[0] != 'ok':
1323
1334
            raise errors.UnexpectedSmartServerResponse(response)
 
1335
        ok, branch_token, repo_token = response
 
1336
        return branch_token, repo_token
1324
1337
            
1325
1338
    def lock_write(self, token=None):
1326
1339
        if not self._lock_mode:
1363
1376
 
1364
1377
    def _unlock(self, branch_token, repo_token):
1365
1378
        path = self.bzrdir._path_for_remote_call(self._client)
1366
 
        response = self._client.call('Branch.unlock', path, branch_token,
1367
 
                                     repo_token or '')
 
1379
        try:
 
1380
            response = self._client.call('Branch.unlock', path, branch_token,
 
1381
                                         repo_token or '')
 
1382
        except errors.ErrorFromSmartServer, err:
 
1383
            if err.error_verb == 'TokenMismatch':
 
1384
                raise errors.TokenMismatch(
 
1385
                    str((branch_token, repo_token)), '(remote tokens)')
 
1386
            raise
1368
1387
        if response == ('ok',):
1369
1388
            return
1370
 
        elif response[0] == 'TokenMismatch':
1371
 
            raise errors.TokenMismatch(
1372
 
                str((branch_token, repo_token)), '(remote tokens)')
1373
 
        else:
1374
 
            raise errors.UnexpectedSmartServerResponse(response)
 
1389
        raise errors.UnexpectedSmartServerResponse(response)
1375
1390
 
1376
1391
    def unlock(self):
1377
1392
        self._lock_count -= 1
1428
1443
    def _gen_revision_history(self):
1429
1444
        """See Branch._gen_revision_history()."""
1430
1445
        path = self.bzrdir._path_for_remote_call(self._client)
1431
 
        response = self._client.call_expecting_body(
 
1446
        response_tuple, response_handler = self._client.call_expecting_body(
1432
1447
            'Branch.revision_history', path)
1433
 
        if response[0][0] != 'ok':
1434
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
1435
 
        result = response[1].read_body_bytes().split('\x00')
 
1448
        if response_tuple[0] != 'ok':
 
1449
            raise UnexpectedSmartServerResponse(response_tuple)
 
1450
        result = response_handler.read_body_bytes().split('\x00')
1436
1451
        if result == ['']:
1437
1452
            return []
1438
1453
        return result
1448
1463
        else:
1449
1464
            rev_id = rev_history[-1]
1450
1465
        self._clear_cached_state()
1451
 
        response = self._client.call('Branch.set_last_revision',
1452
 
            path, self._lock_token, self._repo_lock_token, rev_id)
1453
 
        if response[0] == 'NoSuchRevision':
1454
 
            raise NoSuchRevision(self, rev_id)
1455
 
        elif response[0] != 'ok':
1456
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
1466
        try:
 
1467
            response = self._client.call('Branch.set_last_revision',
 
1468
                path, self._lock_token, self._repo_lock_token, rev_id)
 
1469
        except errors.ErrorFromSmartServer, err:
 
1470
            if err.error_verb == 'NoSuchRevision':
 
1471
                raise NoSuchRevision(self, rev_id)
 
1472
            raise
 
1473
        if response != ('ok',):
 
1474
            raise errors.UnexpectedSmartServerResponse(response)
1457
1475
        self._cache_revision_history(rev_history)
1458
1476
 
1459
1477
    def get_parent(self):
1509
1527
            self._ensure_real()
1510
1528
            self._clear_cached_state()
1511
1529
            return self._real_branch.set_last_revision_info(revno, revision_id)
 
1530
        except errors.ErrorFromSmartServer, err:
 
1531
            if err.error_verb == 'NoSuchRevision':
 
1532
                raise NoSuchRevision(self, err.error_args[0])
 
1533
            raise
1512
1534
        if response == ('ok',):
1513
1535
            self._clear_cached_state()
1514
 
        elif response[0] == 'NoSuchRevision':
1515
 
            raise NoSuchRevision(self, response[1])
1516
1536
        else:
1517
1537
            raise errors.UnexpectedSmartServerResponse(response)
1518
1538