~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Patch Queue Manager
  • Date: 2011-11-28 03:52:43 UTC
  • mfrom: (6305.1.1 fix-dotted-revno)
  • Revision ID: pqm@pqm.ubuntu.com-20111128035243-w2jbl870d8lq94v1
(jelmer) Fix fallback for RemoteBranch.revision_id_to_dotted_revno,
 and add a test. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
    controldir,
25
25
    debug,
26
26
    errors,
 
27
    gpg,
27
28
    graph,
28
29
    lock,
29
30
    lockdir,
 
31
    registry,
30
32
    repository as _mod_repository,
31
33
    revision as _mod_revision,
32
34
    static_tuple,
33
35
    symbol_versioning,
 
36
    testament as _mod_testament,
34
37
    urlutils,
35
38
    vf_repository,
36
39
    )
48
51
from bzrlib.revision import NULL_REVISION
49
52
from bzrlib.revisiontree import InventoryRevisionTree
50
53
from bzrlib.repository import RepositoryWriteLockResult, _LazyListJoin
51
 
from bzrlib.trace import mutter, note, warning
 
54
from bzrlib.trace import mutter, note, warning, log_exception_quietly
52
55
 
53
56
 
54
57
_DEFAULT_SEARCH_DEPTH = 100
333
336
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
334
337
 
335
338
 
 
339
class RemoteControlStore(config.IniFileStore):
 
340
    """Control store which attempts to use HPSS calls to retrieve control store.
 
341
 
 
342
    Note that this is specific to bzr-based formats.
 
343
    """
 
344
 
 
345
    def __init__(self, bzrdir):
 
346
        super(RemoteControlStore, self).__init__()
 
347
        self.bzrdir = bzrdir
 
348
        self._real_store = None
 
349
 
 
350
    def lock_write(self, token=None):
 
351
        self._ensure_real()
 
352
        return self._real_store.lock_write(token)
 
353
 
 
354
    def unlock(self):
 
355
        self._ensure_real()
 
356
        return self._real_store.unlock()
 
357
 
 
358
    @needs_write_lock
 
359
    def save(self):
 
360
        # We need to be able to override the undecorated implementation
 
361
        self.save_without_locking()
 
362
 
 
363
    def save_without_locking(self):
 
364
        super(RemoteControlStore, self).save()
 
365
 
 
366
    def _ensure_real(self):
 
367
        self.bzrdir._ensure_real()
 
368
        if self._real_store is None:
 
369
            self._real_store = config.ControlStore(self.bzrdir)
 
370
 
 
371
    def external_url(self):
 
372
        return self.bzrdir.user_url
 
373
 
 
374
    def _load_content(self):
 
375
        medium = self.bzrdir._client._medium
 
376
        path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
 
377
        try:
 
378
            response, handler = self.bzrdir._call_expecting_body(
 
379
                'BzrDir.get_config_file', path)
 
380
        except errors.UnknownSmartMethod:
 
381
            self._ensure_real()
 
382
            return self._real_store._load_content()
 
383
        if len(response) and response[0] != 'ok':
 
384
            raise errors.UnexpectedSmartServerResponse(response)
 
385
        return handler.read_body_bytes()
 
386
 
 
387
    def _save_content(self, content):
 
388
        # FIXME JRV 2011-11-22: Ideally this should use a
 
389
        # HPSS call too, but at the moment it is not possible
 
390
        # to write lock control directories.
 
391
        self._ensure_real()
 
392
        return self._real_store._save_content(content)
 
393
 
 
394
 
336
395
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
337
396
    """Control directory on a remote server, accessed via bzr:// or similar."""
338
397
 
486
545
 
487
546
    def destroy_repository(self):
488
547
        """See BzrDir.destroy_repository"""
489
 
        self._ensure_real()
490
 
        self._real_bzrdir.destroy_repository()
 
548
        path = self._path_for_remote_call(self._client)
 
549
        try:
 
550
            response = self._call('BzrDir.destroy_repository', path)
 
551
        except errors.UnknownSmartMethod:
 
552
            self._ensure_real()
 
553
            self._real_bzrdir.destroy_repository()
 
554
            return
 
555
        if response[0] != 'ok':
 
556
            raise SmartProtocolError('unexpected response code %s' % (response,))
491
557
 
492
558
    def create_branch(self, name=None, repository=None,
493
559
                      append_revisions_only=None):
515
581
 
516
582
    def destroy_branch(self, name=None):
517
583
        """See BzrDir.destroy_branch"""
518
 
        self._ensure_real()
519
 
        self._real_bzrdir.destroy_branch(name=name)
 
584
        path = self._path_for_remote_call(self._client)
 
585
        try:
 
586
            if name is not None:
 
587
                args = (name, )
 
588
            else:
 
589
                args = ()
 
590
            response = self._call('BzrDir.destroy_branch', path, *args)
 
591
        except errors.UnknownSmartMethod:
 
592
            self._ensure_real()
 
593
            self._real_bzrdir.destroy_branch(name=name)
 
594
            self._next_open_branch_result = None
 
595
            return
520
596
        self._next_open_branch_result = None
 
597
        if response[0] != 'ok':
 
598
            raise SmartProtocolError('unexpected response code %s' % (response,))
521
599
 
522
600
    def create_workingtree(self, revision_id=None, from_branch=None,
523
601
        accelerator_tree=None, hardlink=False):
665
743
 
666
744
    def has_workingtree(self):
667
745
        if self._has_working_tree is None:
668
 
            self._ensure_real()
669
 
            self._has_working_tree = self._real_bzrdir.has_workingtree()
 
746
            path = self._path_for_remote_call(self._client)
 
747
            try:
 
748
                response = self._call('BzrDir.has_workingtree', path)
 
749
            except errors.UnknownSmartMethod:
 
750
                self._ensure_real()
 
751
                self._has_working_tree = self._real_bzrdir.has_workingtree()
 
752
            else:
 
753
                if response[0] not in ('yes', 'no'):
 
754
                    raise SmartProtocolError('unexpected response code %s' % (response,))
 
755
                self._has_working_tree = (response[0] == 'yes')
670
756
        return self._has_working_tree
671
757
 
672
758
    def open_workingtree(self, recommend_upgrade=True):
703
789
    def _get_config(self):
704
790
        return RemoteBzrDirConfig(self)
705
791
 
 
792
    def _get_config_store(self):
 
793
        return RemoteControlStore(self)
 
794
 
706
795
 
707
796
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
708
797
    """Format for repositories accessed over a _SmartClient.
918
1007
        return self._custom_format._serializer
919
1008
 
920
1009
 
921
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
922
 
    controldir.ControlComponent):
 
1010
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
 
1011
        lock._RelockDebugMixin):
923
1012
    """Repository accessed over rpc.
924
1013
 
925
1014
    For the moment most operations are performed using local transport-backed
949
1038
        self._format = format
950
1039
        self._lock_mode = None
951
1040
        self._lock_token = None
 
1041
        self._write_group_tokens = None
952
1042
        self._lock_count = 0
953
1043
        self._leave_lock = False
954
1044
        # Cache of revision parents; misses are cached during read locks, and
994
1084
 
995
1085
        :param suppress_errors: see Repository.abort_write_group.
996
1086
        """
997
 
        self._ensure_real()
998
 
        return self._real_repository.abort_write_group(
999
 
            suppress_errors=suppress_errors)
 
1087
        if self._real_repository:
 
1088
            self._ensure_real()
 
1089
            return self._real_repository.abort_write_group(
 
1090
                suppress_errors=suppress_errors)
 
1091
        if not self.is_in_write_group():
 
1092
            if suppress_errors:
 
1093
                mutter('(suppressed) not in write group')
 
1094
                return
 
1095
            raise errors.BzrError("not in write group")
 
1096
        path = self.bzrdir._path_for_remote_call(self._client)
 
1097
        try:
 
1098
            response = self._call('Repository.abort_write_group', path,
 
1099
                self._lock_token, self._write_group_tokens)
 
1100
        except Exception, exc:
 
1101
            self._write_group = None
 
1102
            if not suppress_errors:
 
1103
                raise
 
1104
            mutter('abort_write_group failed')
 
1105
            log_exception_quietly()
 
1106
            note(gettext('bzr: ERROR (ignored): %s'), exc)
 
1107
        else:
 
1108
            if response != ('ok', ):
 
1109
                raise errors.UnexpectedSmartServerResponse(response)
 
1110
            self._write_group_tokens = None
1000
1111
 
1001
1112
    @property
1002
1113
    def chk_bytes(self):
1016
1127
        for older plugins that don't use e.g. the CommitBuilder
1017
1128
        facility.
1018
1129
        """
1019
 
        self._ensure_real()
1020
 
        return self._real_repository.commit_write_group()
 
1130
        if self._real_repository:
 
1131
            self._ensure_real()
 
1132
            return self._real_repository.commit_write_group()
 
1133
        if not self.is_in_write_group():
 
1134
            raise errors.BzrError("not in write group")
 
1135
        path = self.bzrdir._path_for_remote_call(self._client)
 
1136
        response = self._call('Repository.commit_write_group', path,
 
1137
            self._lock_token, self._write_group_tokens)
 
1138
        if response != ('ok', ):
 
1139
            raise errors.UnexpectedSmartServerResponse(response)
 
1140
        self._write_group_tokens = None
1021
1141
 
1022
1142
    def resume_write_group(self, tokens):
1023
 
        self._ensure_real()
1024
 
        return self._real_repository.resume_write_group(tokens)
 
1143
        if self._real_repository:
 
1144
            return self._real_repository.resume_write_group(tokens)
 
1145
        path = self.bzrdir._path_for_remote_call(self._client)
 
1146
        try:
 
1147
            response = self._call('Repository.check_write_group', path,
 
1148
               self._lock_token, tokens)
 
1149
        except errors.UnknownSmartMethod:
 
1150
            self._ensure_real()
 
1151
            return self._real_repository.resume_write_group(tokens)
 
1152
        if response != ('ok', ):
 
1153
            raise errors.UnexpectedSmartServerResponse(response)
 
1154
        self._write_group_tokens = tokens
1025
1155
 
1026
1156
    def suspend_write_group(self):
1027
 
        self._ensure_real()
1028
 
        return self._real_repository.suspend_write_group()
 
1157
        if self._real_repository:
 
1158
            return self._real_repository.suspend_write_group()
 
1159
        ret = self._write_group_tokens or []
 
1160
        self._write_group_tokens = None
 
1161
        return ret
1029
1162
 
1030
1163
    def get_missing_parent_inventories(self, check_for_missing_texts=True):
1031
1164
        self._ensure_real()
1241
1374
 
1242
1375
    def get_physical_lock_status(self):
1243
1376
        """See Repository.get_physical_lock_status()."""
1244
 
        # should be an API call to the server.
1245
 
        self._ensure_real()
1246
 
        return self._real_repository.get_physical_lock_status()
 
1377
        path = self.bzrdir._path_for_remote_call(self._client)
 
1378
        try:
 
1379
            response = self._call('Repository.get_physical_lock_status', path)
 
1380
        except errors.UnknownSmartMethod:
 
1381
            self._ensure_real()
 
1382
            return self._real_repository.get_physical_lock_status()
 
1383
        if response[0] not in ('yes', 'no'):
 
1384
            raise errors.UnexpectedSmartServerResponse(response)
 
1385
        return (response[0] == 'yes')
1247
1386
 
1248
1387
    def is_in_write_group(self):
1249
1388
        """Return True if there is an open write group.
1250
1389
 
1251
1390
        write groups are only applicable locally for the smart server..
1252
1391
        """
 
1392
        if self._write_group_tokens is not None:
 
1393
            return True
1253
1394
        if self._real_repository:
1254
1395
            return self._real_repository.is_in_write_group()
1255
1396
 
1390
1531
            self._real_repository.lock_write(self._lock_token)
1391
1532
        elif self._lock_mode == 'r':
1392
1533
            self._real_repository.lock_read()
 
1534
        if self._write_group_tokens is not None:
 
1535
            # if we are already in a write group, resume it
 
1536
            self._real_repository.resume_write_group(self._write_group_tokens)
 
1537
            self._write_group_tokens = None
1393
1538
 
1394
1539
    def start_write_group(self):
1395
1540
        """Start a write group on the decorated repository.
1399
1544
        for older plugins that don't use e.g. the CommitBuilder
1400
1545
        facility.
1401
1546
        """
1402
 
        self._ensure_real()
1403
 
        return self._real_repository.start_write_group()
 
1547
        if self._real_repository:
 
1548
            self._ensure_real()
 
1549
            return self._real_repository.start_write_group()
 
1550
        if not self.is_write_locked():
 
1551
            raise errors.NotWriteLocked(self)
 
1552
        if self._write_group_tokens is not None:
 
1553
            raise errors.BzrError('already in a write group')
 
1554
        path = self.bzrdir._path_for_remote_call(self._client)
 
1555
        try:
 
1556
            response = self._call('Repository.start_write_group', path,
 
1557
                self._lock_token)
 
1558
        except (errors.UnknownSmartMethod, errors.UnsuspendableWriteGroup):
 
1559
            self._ensure_real()
 
1560
            return self._real_repository.start_write_group()
 
1561
        if response[0] != 'ok':
 
1562
            raise errors.UnexpectedSmartServerResponse(response)
 
1563
        self._write_group_tokens = response[1]
1404
1564
 
1405
1565
    def _unlock(self, token):
1406
1566
        path = self.bzrdir._path_for_remote_call(self._client)
1433
1593
            # This is just to let the _real_repository stay up to date.
1434
1594
            if self._real_repository is not None:
1435
1595
                self._real_repository.unlock()
 
1596
            elif self._write_group_tokens is not None:
 
1597
                self.abort_write_group()
1436
1598
        finally:
1437
1599
            # The rpc-level lock should be released even if there was a
1438
1600
            # problem releasing the vfs-based lock.
1450
1612
 
1451
1613
    def break_lock(self):
1452
1614
        # should hand off to the network
1453
 
        self._ensure_real()
1454
 
        return self._real_repository.break_lock()
 
1615
        path = self.bzrdir._path_for_remote_call(self._client)
 
1616
        try:
 
1617
            response = self._call("Repository.break_lock", path)
 
1618
        except errors.UnknownSmartMethod:
 
1619
            self._ensure_real()
 
1620
            return self._real_repository.break_lock()
 
1621
        if response != ('ok',):
 
1622
            raise errors.UnexpectedSmartServerResponse(response)
1455
1623
 
1456
1624
    def _get_tarball(self, compression):
1457
1625
        """Return a TemporaryFile containing a repository tarball.
1510
1678
            return list(self.revision_trees([revision_id]))[0]
1511
1679
 
1512
1680
    def get_serializer_format(self):
1513
 
        self._ensure_real()
1514
 
        return self._real_repository.get_serializer_format()
 
1681
        path = self.bzrdir._path_for_remote_call(self._client)
 
1682
        try:
 
1683
            response = self._call('VersionedFileRepository.get_serializer_format',
 
1684
                path)
 
1685
        except errors.UnknownSmartMethod:
 
1686
            self._ensure_real()
 
1687
            return self._real_repository.get_serializer_format()
 
1688
        if response[0] != 'ok':
 
1689
            raise errors.UnexpectedSmartServerResponse(response)
 
1690
        return response[1]
1515
1691
 
1516
1692
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1517
1693
                           timezone=None, committer=None, revprops=None,
1865
2041
        return self._real_repository.reconcile(other=other, thorough=thorough)
1866
2042
 
1867
2043
    def all_revision_ids(self):
1868
 
        self._ensure_real()
1869
 
        return self._real_repository.all_revision_ids()
 
2044
        path = self.bzrdir._path_for_remote_call(self._client)
 
2045
        try:
 
2046
            response_tuple, response_handler = self._call_expecting_body(
 
2047
                "Repository.all_revision_ids", path)
 
2048
        except errors.UnknownSmartMethod:
 
2049
            self._ensure_real()
 
2050
            return self._real_repository.all_revision_ids()
 
2051
        if response_tuple != ("ok", ):
 
2052
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2053
        revids = set(response_handler.read_body_bytes().splitlines())
 
2054
        for fallback in self._fallback_repositories:
 
2055
            revids.update(set(fallback.all_revision_ids()))
 
2056
        return list(revids)
1870
2057
 
1871
2058
    @needs_read_lock
1872
2059
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1992
2179
 
1993
2180
    @needs_write_lock
1994
2181
    def sign_revision(self, revision_id, gpg_strategy):
1995
 
        self._ensure_real()
1996
 
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
 
2182
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2183
        plaintext = testament.as_short_text()
 
2184
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1997
2185
 
1998
2186
    @property
1999
2187
    def texts(self):
2028
2216
        self.add_signature_text(revision_id, signature)
2029
2217
 
2030
2218
    def add_signature_text(self, revision_id, signature):
2031
 
        self._ensure_real()
2032
 
        return self._real_repository.add_signature_text(revision_id, signature)
 
2219
        if self._real_repository:
 
2220
            # If there is a real repository the write group will
 
2221
            # be in the real repository as well, so use that:
 
2222
            self._ensure_real()
 
2223
            return self._real_repository.add_signature_text(
 
2224
                revision_id, signature)
 
2225
        path = self.bzrdir._path_for_remote_call(self._client)
 
2226
        response, response_handler = self._call_with_body_bytes(
 
2227
            'Repository.add_signature_text', (path, revision_id),
 
2228
            signature)
 
2229
        self.refresh_data()
 
2230
        if response[0] != 'ok':
 
2231
            raise errors.UnexpectedSmartServerResponse(response)
2033
2232
 
2034
2233
    def has_signature_for_revision_id(self, revision_id):
2035
2234
        path = self.bzrdir._path_for_remote_call(self._client)
2044
2243
            raise SmartProtocolError('unexpected response code %s' % (response,))
2045
2244
        return (response[0] == 'yes')
2046
2245
 
 
2246
    @needs_read_lock
2047
2247
    def verify_revision_signature(self, revision_id, gpg_strategy):
2048
 
        self._ensure_real()
2049
 
        return self._real_repository.verify_revision_signature(
2050
 
            revision_id, gpg_strategy)
 
2248
        if not self.has_signature_for_revision_id(revision_id):
 
2249
            return gpg.SIGNATURE_NOT_SIGNED, None
 
2250
        signature = self.get_signature_text(revision_id)
 
2251
 
 
2252
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2253
        plaintext = testament.as_short_text()
 
2254
 
 
2255
        return gpg_strategy.verify(signature, plaintext)
2051
2256
 
2052
2257
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2053
2258
        self._ensure_real()
2523
2728
                return True
2524
2729
        return False
2525
2730
 
 
2731
 
 
2732
class RemoteBranchStore(config.IniFileStore):
 
2733
    """Branch store which attempts to use HPSS calls to retrieve branch store.
 
2734
 
 
2735
    Note that this is specific to bzr-based formats.
 
2736
    """
 
2737
 
 
2738
    def __init__(self, branch):
 
2739
        super(RemoteBranchStore, self).__init__()
 
2740
        self.branch = branch
 
2741
        self.id = "branch"
 
2742
        self._real_store = None
 
2743
 
 
2744
    def lock_write(self, token=None):
 
2745
        return self.branch.lock_write(token)
 
2746
 
 
2747
    def unlock(self):
 
2748
        return self.branch.unlock()
 
2749
 
 
2750
    @needs_write_lock
 
2751
    def save(self):
 
2752
        # We need to be able to override the undecorated implementation
 
2753
        self.save_without_locking()
 
2754
 
 
2755
    def save_without_locking(self):
 
2756
        super(RemoteBranchStore, self).save()
 
2757
 
 
2758
    def external_url(self):
 
2759
        return self.branch.user_url
 
2760
 
 
2761
    def _load_content(self):
 
2762
        path = self.branch._remote_path()
 
2763
        try:
 
2764
            response, handler = self.branch._call_expecting_body(
 
2765
                'Branch.get_config_file', path)
 
2766
        except errors.UnknownSmartMethod:
 
2767
            self._ensure_real()
 
2768
            return self._real_store._load_content()
 
2769
        if len(response) and response[0] != 'ok':
 
2770
            raise errors.UnexpectedSmartServerResponse(response)
 
2771
        return handler.read_body_bytes()
 
2772
 
 
2773
    def _save_content(self, content):
 
2774
        path = self.branch._remote_path()
 
2775
        try:
 
2776
            response, handler = self.branch._call_with_body_bytes_expecting_body(
 
2777
                'Branch.put_config_file', (path,
 
2778
                    self.branch._lock_token, self.branch._repo_lock_token),
 
2779
                content)
 
2780
        except errors.UnknownSmartMethod:
 
2781
            self._ensure_real()
 
2782
            return self._real_store._save_content(content)
 
2783
        handler.cancel_read_body()
 
2784
        if response != ('ok', ):
 
2785
            raise errors.UnexpectedSmartServerResponse(response)
 
2786
 
 
2787
    def _ensure_real(self):
 
2788
        self.branch._ensure_real()
 
2789
        if self._real_store is None:
 
2790
            self._real_store = config.BranchStore(self.branch)
 
2791
 
 
2792
 
2526
2793
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2527
2794
    """Branch stored on a server accessed by HPSS RPC.
2528
2795
 
2617
2884
    def _get_config(self):
2618
2885
        return RemoteBranchConfig(self)
2619
2886
 
 
2887
    def _get_config_store(self):
 
2888
        return RemoteBranchStore(self)
 
2889
 
2620
2890
    def _get_real_transport(self):
2621
2891
        # if we try vfs access, return the real branch's vfs transport
2622
2892
        self._ensure_real()
2698
2968
 
2699
2969
    def get_physical_lock_status(self):
2700
2970
        """See Branch.get_physical_lock_status()."""
2701
 
        # should be an API call to the server, as branches must be lockable.
2702
 
        self._ensure_real()
2703
 
        return self._real_branch.get_physical_lock_status()
 
2971
        try:
 
2972
            response = self._client.call('Branch.get_physical_lock_status',
 
2973
                self._remote_path())
 
2974
        except errors.UnknownSmartMethod:
 
2975
            self._ensure_real()
 
2976
            return self._real_branch.get_physical_lock_status()
 
2977
        if response[0] not in ('yes', 'no'):
 
2978
            raise errors.UnexpectedSmartServerResponse(response)
 
2979
        return (response[0] == 'yes')
2704
2980
 
2705
2981
    def get_stacked_on_url(self):
2706
2982
        """Get the URL this branch is stacked against.
2890
3166
            self.repository.unlock()
2891
3167
 
2892
3168
    def break_lock(self):
2893
 
        self._ensure_real()
2894
 
        return self._real_branch.break_lock()
 
3169
        try:
 
3170
            response = self._call(
 
3171
                'Branch.break_lock', self._remote_path())
 
3172
        except errors.UnknownSmartMethod:
 
3173
            self._ensure_real()
 
3174
            return self._real_branch.break_lock()
 
3175
        if response != ('ok',):
 
3176
            raise errors.UnexpectedSmartServerResponse(response)
2895
3177
 
2896
3178
    def leave_lock_in_place(self):
2897
3179
        if not self._lock_token:
3071
3353
        return self._lock_count >= 1
3072
3354
 
3073
3355
    @needs_read_lock
 
3356
    def revision_id_to_dotted_revno(self, revision_id):
 
3357
        """Given a revision id, return its dotted revno.
 
3358
 
 
3359
        :return: a tuple like (1,) or (400,1,3).
 
3360
        """
 
3361
        try:
 
3362
            response = self._call('Branch.revision_id_to_revno',
 
3363
                self._remote_path(), revision_id)
 
3364
        except errors.UnknownSmartMethod:
 
3365
            self._ensure_real()
 
3366
            return self._real_branch.revision_id_to_dotted_revno(revision_id)
 
3367
        if response[0] == 'ok':
 
3368
            return tuple([int(x) for x in response[1:]])
 
3369
        else:
 
3370
            raise errors.UnexpectedSmartServerResponse(response)
 
3371
 
 
3372
    @needs_read_lock
3074
3373
    def revision_id_to_revno(self, revision_id):
3075
 
        self._ensure_real()
3076
 
        return self._real_branch.revision_id_to_revno(revision_id)
 
3374
        """Given a revision id on the branch mainline, return its revno.
 
3375
 
 
3376
        :return: an integer
 
3377
        """
 
3378
        try:
 
3379
            response = self._call('Branch.revision_id_to_revno',
 
3380
                self._remote_path(), revision_id)
 
3381
        except errors.UnknownSmartMethod:
 
3382
            self._ensure_real()
 
3383
            return self._real_branch.revision_id_to_revno(revision_id)
 
3384
        if response[0] == 'ok':
 
3385
            if len(response) == 2:
 
3386
                return int(response[1])
 
3387
            raise NoSuchRevision(self, revision_id)
 
3388
        else:
 
3389
            raise errors.UnexpectedSmartServerResponse(response)
3077
3390
 
3078
3391
    @needs_write_lock
3079
3392
    def set_last_revision_info(self, revno, revision_id):
3318
3631
        tar.extract(tarinfo, to_dir)
3319
3632
 
3320
3633
 
 
3634
error_translators = registry.Registry()
 
3635
no_context_error_translators = registry.Registry()
 
3636
 
 
3637
 
3321
3638
def _translate_error(err, **context):
3322
3639
    """Translate an ErrorFromSmartServer into a more useful error.
3323
3640
 
3352
3669
                    'Missing key %r in context %r', key_err.args[0], context)
3353
3670
                raise err
3354
3671
 
3355
 
    if err.error_verb == 'NoSuchRevision':
3356
 
        raise NoSuchRevision(find('branch'), err.error_args[0])
3357
 
    elif err.error_verb == 'nosuchrevision':
3358
 
        raise NoSuchRevision(find('repository'), err.error_args[0])
3359
 
    elif err.error_verb == 'nobranch':
3360
 
        if len(err.error_args) >= 1:
3361
 
            extra = err.error_args[0]
3362
 
        else:
3363
 
            extra = None
3364
 
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
3365
 
            detail=extra)
3366
 
    elif err.error_verb == 'norepository':
3367
 
        raise errors.NoRepositoryPresent(find('bzrdir'))
3368
 
    elif err.error_verb == 'UnlockableTransport':
3369
 
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
3370
 
    elif err.error_verb == 'TokenMismatch':
3371
 
        raise errors.TokenMismatch(find('token'), '(remote token)')
3372
 
    elif err.error_verb == 'Diverged':
3373
 
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
3374
 
    elif err.error_verb == 'NotStacked':
3375
 
        raise errors.NotStacked(branch=find('branch'))
3376
 
    elif err.error_verb == 'PermissionDenied':
3377
 
        path = get_path()
3378
 
        if len(err.error_args) >= 2:
3379
 
            extra = err.error_args[1]
3380
 
        else:
3381
 
            extra = None
3382
 
        raise errors.PermissionDenied(path, extra=extra)
3383
 
    elif err.error_verb == 'ReadError':
3384
 
        path = get_path()
3385
 
        raise errors.ReadError(path)
3386
 
    elif err.error_verb == 'NoSuchFile':
3387
 
        path = get_path()
3388
 
        raise errors.NoSuchFile(path)
3389
 
    _translate_error_without_context(err)
3390
 
 
3391
 
 
3392
 
def _translate_error_without_context(err):
3393
 
    """Translate any ErrorFromSmartServer values that don't require context"""
3394
 
    if err.error_verb == 'IncompatibleRepositories':
3395
 
        raise errors.IncompatibleRepositories(err.error_args[0],
3396
 
            err.error_args[1], err.error_args[2])
3397
 
    elif err.error_verb == 'LockContention':
3398
 
        raise errors.LockContention('(remote lock)')
3399
 
    elif err.error_verb == 'LockFailed':
3400
 
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
3401
 
    elif err.error_verb == 'TipChangeRejected':
3402
 
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
3403
 
    elif err.error_verb == 'UnstackableBranchFormat':
3404
 
        raise errors.UnstackableBranchFormat(*err.error_args)
3405
 
    elif err.error_verb == 'UnstackableRepositoryFormat':
3406
 
        raise errors.UnstackableRepositoryFormat(*err.error_args)
3407
 
    elif err.error_verb == 'FileExists':
3408
 
        raise errors.FileExists(err.error_args[0])
3409
 
    elif err.error_verb == 'DirectoryNotEmpty':
3410
 
        raise errors.DirectoryNotEmpty(err.error_args[0])
3411
 
    elif err.error_verb == 'ShortReadvError':
3412
 
        args = err.error_args
3413
 
        raise errors.ShortReadvError(
3414
 
            args[0], int(args[1]), int(args[2]), int(args[3]))
3415
 
    elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
 
3672
    try:
 
3673
        translator = error_translators.get(err.error_verb)
 
3674
    except KeyError:
 
3675
        pass
 
3676
    else:
 
3677
        raise translator(err, find, get_path)
 
3678
    try:
 
3679
        translator = no_context_error_translators.get(err.error_verb)
 
3680
    except KeyError:
 
3681
        raise errors.UnknownErrorFromSmartServer(err)
 
3682
    else:
 
3683
        raise translator(err)
 
3684
 
 
3685
 
 
3686
error_translators.register('NoSuchRevision',
 
3687
    lambda err, find, get_path: NoSuchRevision(
 
3688
        find('branch'), err.error_args[0]))
 
3689
error_translators.register('nosuchrevision',
 
3690
    lambda err, find, get_path: NoSuchRevision(
 
3691
        find('repository'), err.error_args[0]))
 
3692
 
 
3693
def _translate_nobranch_error(err, find, get_path):
 
3694
    if len(err.error_args) >= 1:
 
3695
        extra = err.error_args[0]
 
3696
    else:
 
3697
        extra = None
 
3698
    return errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
3699
        detail=extra)
 
3700
 
 
3701
error_translators.register('nobranch', _translate_nobranch_error)
 
3702
error_translators.register('norepository',
 
3703
    lambda err, find, get_path: errors.NoRepositoryPresent(
 
3704
        find('bzrdir')))
 
3705
error_translators.register('UnlockableTransport',
 
3706
    lambda err, find, get_path: errors.UnlockableTransport(
 
3707
        find('bzrdir').root_transport))
 
3708
error_translators.register('TokenMismatch',
 
3709
    lambda err, find, get_path: errors.TokenMismatch(
 
3710
        find('token'), '(remote token)'))
 
3711
error_translators.register('Diverged',
 
3712
    lambda err, find, get_path: errors.DivergedBranches(
 
3713
        find('branch'), find('other_branch')))
 
3714
error_translators.register('NotStacked',
 
3715
    lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
 
3716
 
 
3717
def _translate_PermissionDenied(err, find, get_path):
 
3718
    path = get_path()
 
3719
    if len(err.error_args) >= 2:
 
3720
        extra = err.error_args[1]
 
3721
    else:
 
3722
        extra = None
 
3723
    return errors.PermissionDenied(path, extra=extra)
 
3724
 
 
3725
error_translators.register('PermissionDenied', _translate_PermissionDenied)
 
3726
error_translators.register('ReadError',
 
3727
    lambda err, find, get_path: errors.ReadError(get_path()))
 
3728
error_translators.register('NoSuchFile',
 
3729
    lambda err, find, get_path: errors.NoSuchFile(get_path()))
 
3730
no_context_error_translators.register('IncompatibleRepositories',
 
3731
    lambda err: errors.IncompatibleRepositories(
 
3732
        err.error_args[0], err.error_args[1], err.error_args[2]))
 
3733
no_context_error_translators.register('LockContention',
 
3734
    lambda err: errors.LockContention('(remote lock)'))
 
3735
no_context_error_translators.register('LockFailed',
 
3736
    lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
 
3737
no_context_error_translators.register('TipChangeRejected',
 
3738
    lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
 
3739
no_context_error_translators.register('UnstackableBranchFormat',
 
3740
    lambda err: errors.UnstackableBranchFormat(*err.error_args))
 
3741
no_context_error_translators.register('UnstackableRepositoryFormat',
 
3742
    lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
 
3743
no_context_error_translators.register('FileExists',
 
3744
    lambda err: errors.FileExists(err.error_args[0]))
 
3745
no_context_error_translators.register('DirectoryNotEmpty',
 
3746
    lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
 
3747
 
 
3748
def _translate_short_readv_error(err):
 
3749
    args = err.error_args
 
3750
    return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
 
3751
        int(args[3]))
 
3752
 
 
3753
no_context_error_translators.register('ShortReadvError',
 
3754
    _translate_short_readv_error)
 
3755
 
 
3756
def _translate_unicode_error(err):
3416
3757
        encoding = str(err.error_args[0]) # encoding must always be a string
3417
3758
        val = err.error_args[1]
3418
3759
        start = int(err.error_args[2])
3426
3767
            raise UnicodeDecodeError(encoding, val, start, end, reason)
3427
3768
        elif err.error_verb == 'UnicodeEncodeError':
3428
3769
            raise UnicodeEncodeError(encoding, val, start, end, reason)
3429
 
    elif err.error_verb == 'ReadOnlyError':
3430
 
        raise errors.TransportNotPossible('readonly transport')
3431
 
    elif err.error_verb == 'MemoryError':
3432
 
        raise errors.BzrError("remote server out of memory\n"
3433
 
            "Retry non-remotely, or contact the server admin for details.")
3434
 
    raise errors.UnknownErrorFromSmartServer(err)
 
3770
 
 
3771
no_context_error_translators.register('UnicodeEncodeError',
 
3772
    _translate_unicode_error)
 
3773
no_context_error_translators.register('UnicodeDecodeError',
 
3774
    _translate_unicode_error)
 
3775
no_context_error_translators.register('ReadOnlyError',
 
3776
    lambda err: errors.TransportNotPossible('readonly transport'))
 
3777
no_context_error_translators.register('MemoryError',
 
3778
    lambda err: errors.BzrError("remote server out of memory\n"
 
3779
        "Retry non-remotely, or contact the server admin for details."))
 
3780
 
 
3781
no_context_error_translators.register('BzrCheckError',
 
3782
    lambda err: errors.BzrCheckError(msg=err.error_args[0]))
 
3783
 
 
3784
error_translators.register('UnsuspendableWriteGroup',
 
3785
    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
 
3786
        repository=find('repository')))
 
3787
error_translators.register('UnresumableWriteGroup',
 
3788
    lambda err, find, get_path: errors.UnresumableWriteGroup(
 
3789
        repository=find('repository'), write_groups=err.error_args[0],
 
3790
        reason=err.error_args[1]))