~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-02-11 06:15:33 UTC
  • mfrom: (5025.1.6 331095-malloc)
  • Revision ID: pqm@pqm.ubuntu.com-20100211061533-5glf4faoutadhql9
(mbp) avoid malloc(0)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
19
19
from bzrlib import (
20
20
    bencode,
21
21
    branch,
22
 
    bzrdir as _mod_bzrdir,
 
22
    bzrdir,
23
23
    config,
24
 
    controldir,
25
24
    debug,
26
25
    errors,
27
26
    graph,
28
27
    lock,
29
28
    lockdir,
30
 
    repository as _mod_repository,
 
29
    repository,
 
30
    revision,
31
31
    revision as _mod_revision,
32
 
    static_tuple,
33
32
    symbol_versioning,
34
 
    urlutils,
35
 
    vf_repository,
36
 
    )
37
 
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
33
)
 
34
from bzrlib.branch import BranchReferenceFormat
 
35
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
38
36
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
39
37
from bzrlib.errors import (
40
38
    NoSuchRevision,
41
39
    SmartProtocolError,
42
40
    )
43
 
from bzrlib.i18n import gettext
44
41
from bzrlib.lockable_files import LockableFiles
45
42
from bzrlib.smart import client, vfs, repository as smart_repo
46
 
from bzrlib.smart.client import _SmartClient
47
 
from bzrlib.revision import NULL_REVISION
48
 
from bzrlib.repository import RepositoryWriteLockResult, _LazyListJoin
 
43
from bzrlib.revision import ensure_null, NULL_REVISION
49
44
from bzrlib.trace import mutter, note, warning
50
45
 
51
46
 
52
 
_DEFAULT_SEARCH_DEPTH = 100
53
 
 
54
 
 
55
47
class _RpcHelper(object):
56
48
    """Mixin class that helps with issuing RPCs."""
57
49
 
92
84
    return format
93
85
 
94
86
 
95
 
# Note that RemoteBzrDirProber lives in bzrlib.bzrdir so bzrlib.remote
96
 
# does not have to be imported unless a remote format is involved.
97
 
 
98
 
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
99
 
    """Format representing bzrdirs accessed via a smart server"""
100
 
 
101
 
    supports_workingtrees = False
102
 
 
103
 
    def __init__(self):
104
 
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
105
 
        # XXX: It's a bit ugly that the network name is here, because we'd
106
 
        # like to believe that format objects are stateless or at least
107
 
        # immutable,  However, we do at least avoid mutating the name after
108
 
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
109
 
        self._network_name = None
110
 
 
111
 
    def __repr__(self):
112
 
        return "%s(_network_name=%r)" % (self.__class__.__name__,
113
 
            self._network_name)
114
 
 
115
 
    def get_format_description(self):
116
 
        if self._network_name:
117
 
            real_format = controldir.network_format_registry.get(self._network_name)
118
 
            return 'Remote: ' + real_format.get_format_description()
119
 
        return 'bzr remote bzrdir'
120
 
 
121
 
    def get_format_string(self):
122
 
        raise NotImplementedError(self.get_format_string)
123
 
 
124
 
    def network_name(self):
125
 
        if self._network_name:
126
 
            return self._network_name
127
 
        else:
128
 
            raise AssertionError("No network name set.")
129
 
 
130
 
    def initialize_on_transport(self, transport):
131
 
        try:
132
 
            # hand off the request to the smart server
133
 
            client_medium = transport.get_smart_medium()
134
 
        except errors.NoSmartMedium:
135
 
            # TODO: lookup the local format from a server hint.
136
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
137
 
            return local_dir_format.initialize_on_transport(transport)
138
 
        client = _SmartClient(client_medium)
139
 
        path = client.remote_path_from_transport(transport)
140
 
        try:
141
 
            response = client.call('BzrDirFormat.initialize', path)
142
 
        except errors.ErrorFromSmartServer, err:
143
 
            _translate_error(err, path=path)
144
 
        if response[0] != 'ok':
145
 
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
146
 
        format = RemoteBzrDirFormat()
147
 
        self._supply_sub_formats_to(format)
148
 
        return RemoteBzrDir(transport, format)
149
 
 
150
 
    def parse_NoneTrueFalse(self, arg):
151
 
        if not arg:
152
 
            return None
153
 
        if arg == 'False':
154
 
            return False
155
 
        if arg == 'True':
156
 
            return True
157
 
        raise AssertionError("invalid arg %r" % arg)
158
 
 
159
 
    def _serialize_NoneTrueFalse(self, arg):
160
 
        if arg is False:
161
 
            return 'False'
162
 
        if arg:
163
 
            return 'True'
164
 
        return ''
165
 
 
166
 
    def _serialize_NoneString(self, arg):
167
 
        return arg or ''
168
 
 
169
 
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
170
 
        create_prefix=False, force_new_repo=False, stacked_on=None,
171
 
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
172
 
        shared_repo=False):
173
 
        try:
174
 
            # hand off the request to the smart server
175
 
            client_medium = transport.get_smart_medium()
176
 
        except errors.NoSmartMedium:
177
 
            do_vfs = True
178
 
        else:
179
 
            # Decline to open it if the server doesn't support our required
180
 
            # version (3) so that the VFS-based transport will do it.
181
 
            if client_medium.should_probe():
182
 
                try:
183
 
                    server_version = client_medium.protocol_version()
184
 
                    if server_version != '2':
185
 
                        do_vfs = True
186
 
                    else:
187
 
                        do_vfs = False
188
 
                except errors.SmartProtocolError:
189
 
                    # Apparently there's no usable smart server there, even though
190
 
                    # the medium supports the smart protocol.
191
 
                    do_vfs = True
192
 
            else:
193
 
                do_vfs = False
194
 
        if not do_vfs:
195
 
            client = _SmartClient(client_medium)
196
 
            path = client.remote_path_from_transport(transport)
197
 
            if client_medium._is_remote_before((1, 16)):
198
 
                do_vfs = True
199
 
        if do_vfs:
200
 
            # TODO: lookup the local format from a server hint.
201
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
202
 
            self._supply_sub_formats_to(local_dir_format)
203
 
            return local_dir_format.initialize_on_transport_ex(transport,
204
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
205
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
206
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
207
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
208
 
                vfs_only=True)
209
 
        return self._initialize_on_transport_ex_rpc(client, path, transport,
210
 
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
211
 
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
212
 
 
213
 
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
214
 
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
215
 
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
216
 
        args = []
217
 
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
218
 
        args.append(self._serialize_NoneTrueFalse(create_prefix))
219
 
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
220
 
        args.append(self._serialize_NoneString(stacked_on))
221
 
        # stack_on_pwd is often/usually our transport
222
 
        if stack_on_pwd:
223
 
            try:
224
 
                stack_on_pwd = transport.relpath(stack_on_pwd)
225
 
                if not stack_on_pwd:
226
 
                    stack_on_pwd = '.'
227
 
            except errors.PathNotChild:
228
 
                pass
229
 
        args.append(self._serialize_NoneString(stack_on_pwd))
230
 
        args.append(self._serialize_NoneString(repo_format_name))
231
 
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
232
 
        args.append(self._serialize_NoneTrueFalse(shared_repo))
233
 
        request_network_name = self._network_name or \
234
 
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
235
 
        try:
236
 
            response = client.call('BzrDirFormat.initialize_ex_1.16',
237
 
                request_network_name, path, *args)
238
 
        except errors.UnknownSmartMethod:
239
 
            client._medium._remember_remote_is_before((1,16))
240
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
241
 
            self._supply_sub_formats_to(local_dir_format)
242
 
            return local_dir_format.initialize_on_transport_ex(transport,
243
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
244
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
245
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
246
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
247
 
                vfs_only=True)
248
 
        except errors.ErrorFromSmartServer, err:
249
 
            _translate_error(err, path=path)
250
 
        repo_path = response[0]
251
 
        bzrdir_name = response[6]
252
 
        require_stacking = response[7]
253
 
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
254
 
        format = RemoteBzrDirFormat()
255
 
        format._network_name = bzrdir_name
256
 
        self._supply_sub_formats_to(format)
257
 
        bzrdir = RemoteBzrDir(transport, format, _client=client)
258
 
        if repo_path:
259
 
            repo_format = response_tuple_to_repo_format(response[1:])
260
 
            if repo_path == '.':
261
 
                repo_path = ''
262
 
            if repo_path:
263
 
                repo_bzrdir_format = RemoteBzrDirFormat()
264
 
                repo_bzrdir_format._network_name = response[5]
265
 
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
266
 
                    repo_bzrdir_format)
267
 
            else:
268
 
                repo_bzr = bzrdir
269
 
            final_stack = response[8] or None
270
 
            final_stack_pwd = response[9] or None
271
 
            if final_stack_pwd:
272
 
                final_stack_pwd = urlutils.join(
273
 
                    transport.base, final_stack_pwd)
274
 
            remote_repo = RemoteRepository(repo_bzr, repo_format)
275
 
            if len(response) > 10:
276
 
                # Updated server verb that locks remotely.
277
 
                repo_lock_token = response[10] or None
278
 
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
279
 
                if repo_lock_token:
280
 
                    remote_repo.dont_leave_lock_in_place()
281
 
            else:
282
 
                remote_repo.lock_write()
283
 
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
284
 
                final_stack_pwd, require_stacking)
285
 
            policy.acquire_repository()
286
 
        else:
287
 
            remote_repo = None
288
 
            policy = None
289
 
        bzrdir._format.set_branch_format(self.get_branch_format())
290
 
        if require_stacking:
291
 
            # The repo has already been created, but we need to make sure that
292
 
            # we'll make a stackable branch.
293
 
            bzrdir._format.require_stacking(_skip_repo=True)
294
 
        return remote_repo, bzrdir, require_stacking, policy
295
 
 
296
 
    def _open(self, transport):
297
 
        return RemoteBzrDir(transport, self)
298
 
 
299
 
    def __eq__(self, other):
300
 
        if not isinstance(other, RemoteBzrDirFormat):
301
 
            return False
302
 
        return self.get_format_description() == other.get_format_description()
303
 
 
304
 
    def __return_repository_format(self):
305
 
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
306
 
        # repository format has been asked for, tell the RemoteRepositoryFormat
307
 
        # that it should use that for init() etc.
308
 
        result = RemoteRepositoryFormat()
309
 
        custom_format = getattr(self, '_repository_format', None)
310
 
        if custom_format:
311
 
            if isinstance(custom_format, RemoteRepositoryFormat):
312
 
                return custom_format
313
 
            else:
314
 
                # We will use the custom format to create repositories over the
315
 
                # wire; expose its details like rich_root_data for code to
316
 
                # query
317
 
                result._custom_format = custom_format
318
 
        return result
319
 
 
320
 
    def get_branch_format(self):
321
 
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
322
 
        if not isinstance(result, RemoteBranchFormat):
323
 
            new_result = RemoteBranchFormat()
324
 
            new_result._custom_format = result
325
 
            # cache the result
326
 
            self.set_branch_format(new_result)
327
 
            result = new_result
328
 
        return result
329
 
 
330
 
    repository_format = property(__return_repository_format,
331
 
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
332
 
 
333
 
 
334
 
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
 
87
# Note: RemoteBzrDirFormat is in bzrdir.py
 
88
 
 
89
class RemoteBzrDir(BzrDir, _RpcHelper):
335
90
    """Control directory on a remote server, accessed via bzr:// or similar."""
336
91
 
337
92
    def __init__(self, transport, format, _client=None, _force_probe=False):
340
95
        :param _client: Private parameter for testing. Disables probing and the
341
96
            use of a real bzrdir.
342
97
        """
343
 
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
 
98
        BzrDir.__init__(self, transport, format)
344
99
        # this object holds a delegated bzrdir that uses file-level operations
345
100
        # to talk to the other side
346
101
        self._real_bzrdir = None
406
161
                import traceback
407
162
                warning('VFS BzrDir access triggered\n%s',
408
163
                    ''.join(traceback.format_stack()))
409
 
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
 
164
            self._real_bzrdir = BzrDir.open_from_transport(
410
165
                self.root_transport, _server_formats=False)
411
166
            self._format._network_name = \
412
167
                self._real_bzrdir._format.network_name()
418
173
        # Prevent aliasing problems in the next_open_branch_result cache.
419
174
        # See create_branch for rationale.
420
175
        self._next_open_branch_result = None
421
 
        return _mod_bzrdir.BzrDir.break_lock(self)
 
176
        return BzrDir.break_lock(self)
422
177
 
423
178
    def _vfs_cloning_metadir(self, require_stacking=False):
424
179
        self._ensure_real()
455
210
        if len(branch_info) != 2:
456
211
            raise errors.UnexpectedSmartServerResponse(response)
457
212
        branch_ref, branch_name = branch_info
458
 
        format = controldir.network_format_registry.get(control_name)
 
213
        format = bzrdir.network_format_registry.get(control_name)
459
214
        if repo_name:
460
 
            format.repository_format = _mod_repository.network_format_registry.get(
 
215
            format.repository_format = repository.network_format_registry.get(
461
216
                repo_name)
462
217
        if branch_ref == 'ref':
463
218
            # XXX: we need possible_transports here to avoid reopening the
464
219
            # connection to the referenced location
465
 
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
 
220
            ref_bzrdir = BzrDir.open(branch_name)
466
221
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
467
222
            format.set_branch_format(branch_format)
468
223
        elif branch_ref == 'branch':
487
242
        self._ensure_real()
488
243
        self._real_bzrdir.destroy_repository()
489
244
 
490
 
    def create_branch(self, name=None, repository=None,
491
 
                      append_revisions_only=None):
 
245
    def create_branch(self):
492
246
        # as per meta1 formats - just delegate to the format object which may
493
247
        # be parameterised.
494
 
        real_branch = self._format.get_branch_format().initialize(self,
495
 
            name=name, repository=repository,
496
 
            append_revisions_only=append_revisions_only)
 
248
        real_branch = self._format.get_branch_format().initialize(self)
497
249
        if not isinstance(real_branch, RemoteBranch):
498
 
            if not isinstance(repository, RemoteRepository):
499
 
                raise AssertionError(
500
 
                    'need a RemoteRepository to use with RemoteBranch, got %r'
501
 
                    % (repository,))
502
 
            result = RemoteBranch(self, repository, real_branch, name=name)
 
250
            result = RemoteBranch(self, self.find_repository(), real_branch)
503
251
        else:
504
252
            result = real_branch
505
253
        # BzrDir.clone_on_transport() uses the result of create_branch but does
511
259
        self._next_open_branch_result = result
512
260
        return result
513
261
 
514
 
    def destroy_branch(self, name=None):
 
262
    def destroy_branch(self):
515
263
        """See BzrDir.destroy_branch"""
516
264
        self._ensure_real()
517
 
        self._real_bzrdir.destroy_branch(name=name)
 
265
        self._real_bzrdir.destroy_branch()
518
266
        self._next_open_branch_result = None
519
267
 
520
 
    def create_workingtree(self, revision_id=None, from_branch=None,
521
 
        accelerator_tree=None, hardlink=False):
 
268
    def create_workingtree(self, revision_id=None, from_branch=None):
522
269
        raise errors.NotLocalUrl(self.transport.base)
523
270
 
524
 
    def find_branch_format(self, name=None):
 
271
    def find_branch_format(self):
525
272
        """Find the branch 'format' for this bzrdir.
526
273
 
527
274
        This might be a synthetic object for e.g. RemoteBranch and SVN.
528
275
        """
529
 
        b = self.open_branch(name=name)
 
276
        b = self.open_branch()
530
277
        return b._format
531
278
 
532
 
    def get_branch_reference(self, name=None):
 
279
    def get_branch_reference(self):
533
280
        """See BzrDir.get_branch_reference()."""
534
 
        if name is not None:
535
 
            # XXX JRV20100304: Support opening colocated branches
536
 
            raise errors.NoColocatedBranchSupport(self)
537
281
        response = self._get_branch_reference()
538
282
        if response[0] == 'ref':
539
283
            return response[1]
570
314
            raise errors.UnexpectedSmartServerResponse(response)
571
315
        return response
572
316
 
573
 
    def _get_tree_branch(self, name=None):
 
317
    def _get_tree_branch(self):
574
318
        """See BzrDir._get_tree_branch()."""
575
 
        return None, self.open_branch(name=name)
 
319
        return None, self.open_branch()
576
320
 
577
 
    def open_branch(self, name=None, unsupported=False,
578
 
                    ignore_fallbacks=False):
579
 
        if unsupported:
 
321
    def open_branch(self, _unsupported=False, ignore_fallbacks=False):
 
322
        if _unsupported:
580
323
            raise NotImplementedError('unsupported flag support not implemented yet.')
581
324
        if self._next_open_branch_result is not None:
582
325
            # See create_branch for details.
587
330
        if response[0] == 'ref':
588
331
            # a branch reference, use the existing BranchReference logic.
589
332
            format = BranchReferenceFormat()
590
 
            return format.open(self, name=name, _found=True,
591
 
                location=response[1], ignore_fallbacks=ignore_fallbacks)
 
333
            return format.open(self, _found=True, location=response[1],
 
334
                ignore_fallbacks=ignore_fallbacks)
592
335
        branch_format_name = response[1]
593
336
        if not branch_format_name:
594
337
            branch_format_name = None
595
338
        format = RemoteBranchFormat(network_name=branch_format_name)
596
339
        return RemoteBranch(self, self.find_repository(), format=format,
597
 
            setup_stacking=not ignore_fallbacks, name=name)
 
340
            setup_stacking=not ignore_fallbacks)
598
341
 
599
342
    def _open_repo_v1(self, path):
600
343
        verb = 'BzrDir.find_repository'
675
418
 
676
419
    def _path_for_remote_call(self, client):
677
420
        """Return the path to be used for this bzrdir in a remote call."""
678
 
        return urlutils.split_segment_parameters_raw(
679
 
            client.remote_path_from_transport(self.root_transport))[0]
 
421
        return client.remote_path_from_transport(self.root_transport)
680
422
 
681
 
    def get_branch_transport(self, branch_format, name=None):
 
423
    def get_branch_transport(self, branch_format):
682
424
        self._ensure_real()
683
 
        return self._real_bzrdir.get_branch_transport(branch_format, name=name)
 
425
        return self._real_bzrdir.get_branch_transport(branch_format)
684
426
 
685
427
    def get_repository_transport(self, repository_format):
686
428
        self._ensure_real()
694
436
        """Upgrading of remote bzrdirs is not supported yet."""
695
437
        return False
696
438
 
697
 
    def needs_format_conversion(self, format):
 
439
    def needs_format_conversion(self, format=None):
698
440
        """Upgrading of remote bzrdirs is not supported yet."""
 
441
        if format is None:
 
442
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
 
443
                % 'needs_format_conversion(format=None)')
699
444
        return False
700
445
 
701
446
    def clone(self, url, revision_id=None, force_new_repo=False,
708
453
        return RemoteBzrDirConfig(self)
709
454
 
710
455
 
711
 
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
 
456
class RemoteRepositoryFormat(repository.RepositoryFormat):
712
457
    """Format for repositories accessed over a _SmartClient.
713
458
 
714
459
    Instances of this repository are represented by RemoteRepository
729
474
    """
730
475
 
731
476
    _matchingbzrdir = RemoteBzrDirFormat()
732
 
    supports_full_versioned_files = True
733
 
    supports_leaving_lock = True
734
477
 
735
478
    def __init__(self):
736
 
        _mod_repository.RepositoryFormat.__init__(self)
 
479
        repository.RepositoryFormat.__init__(self)
737
480
        self._custom_format = None
738
481
        self._network_name = None
739
482
        self._creating_bzrdir = None
740
 
        self._revision_graph_can_have_wrong_parents = None
741
483
        self._supports_chks = None
742
484
        self._supports_external_lookups = None
743
485
        self._supports_tree_reference = None
744
 
        self._supports_funky_characters = None
745
 
        self._supports_nesting_repositories = None
746
486
        self._rich_root_data = None
747
487
 
748
488
    def __repr__(self):
777
517
        return self._supports_external_lookups
778
518
 
779
519
    @property
780
 
    def supports_funky_characters(self):
781
 
        if self._supports_funky_characters is None:
782
 
            self._ensure_real()
783
 
            self._supports_funky_characters = \
784
 
                self._custom_format.supports_funky_characters
785
 
        return self._supports_funky_characters
786
 
 
787
 
    @property
788
 
    def supports_nesting_repositories(self):
789
 
        if self._supports_nesting_repositories is None:
790
 
            self._ensure_real()
791
 
            self._supports_nesting_repositories = \
792
 
                self._custom_format.supports_nesting_repositories
793
 
        return self._supports_nesting_repositories
794
 
 
795
 
    @property
796
520
    def supports_tree_reference(self):
797
521
        if self._supports_tree_reference is None:
798
522
            self._ensure_real()
800
524
                self._custom_format.supports_tree_reference
801
525
        return self._supports_tree_reference
802
526
 
803
 
    @property
804
 
    def revision_graph_can_have_wrong_parents(self):
805
 
        if self._revision_graph_can_have_wrong_parents is None:
806
 
            self._ensure_real()
807
 
            self._revision_graph_can_have_wrong_parents = \
808
 
                self._custom_format.revision_graph_can_have_wrong_parents
809
 
        return self._revision_graph_can_have_wrong_parents
810
 
 
811
527
    def _vfs_initialize(self, a_bzrdir, shared):
812
528
        """Helper for common code in initialize."""
813
529
        if self._custom_format:
848
564
            network_name = self._network_name
849
565
        else:
850
566
            # Select the current bzrlib default and ask for that.
851
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
567
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
852
568
            reference_format = reference_bzrdir_format.repository_format
853
569
            network_name = reference_format.network_name()
854
570
        # 2) try direct creation via RPC
880
596
 
881
597
    def _ensure_real(self):
882
598
        if self._custom_format is None:
883
 
            self._custom_format = _mod_repository.network_format_registry.get(
 
599
            self._custom_format = repository.network_format_registry.get(
884
600
                self._network_name)
885
601
 
886
602
    @property
922
638
        return self._custom_format._serializer
923
639
 
924
640
 
925
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
926
 
    controldir.ControlComponent):
 
641
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin):
927
642
    """Repository accessed over rpc.
928
643
 
929
644
    For the moment most operations are performed using local transport-backed
972
687
        # Additional places to query for data.
973
688
        self._fallback_repositories = []
974
689
 
975
 
    @property
976
 
    def user_transport(self):
977
 
        return self.bzrdir.user_transport
978
 
 
979
 
    @property
980
 
    def control_transport(self):
981
 
        # XXX: Normally you shouldn't directly get at the remote repository
982
 
        # transport, but I'm not sure it's worth making this method
983
 
        # optional -- mbp 2010-04-21
984
 
        return self.bzrdir.get_repository_transport(None)
985
 
 
986
690
    def __str__(self):
987
691
        return "%s(%s)" % (self.__class__.__name__, self.base)
988
692
 
1096
800
    def find_text_key_references(self):
1097
801
        """Find the text key references within the repository.
1098
802
 
 
803
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
 
804
        revision_ids. Each altered file-ids has the exact revision_ids that
 
805
        altered it listed explicitly.
1099
806
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
1100
807
            to whether they were referred to by the inventory of the
1101
808
            revision_id that they contain. The inventory texts from all present
1119
826
        """Private method for using with old (< 1.2) servers to fallback."""
1120
827
        if revision_id is None:
1121
828
            revision_id = ''
1122
 
        elif _mod_revision.is_null(revision_id):
 
829
        elif revision.is_null(revision_id):
1123
830
            return {}
1124
831
 
1125
832
        path = self.bzrdir._path_for_remote_call(self._client)
1149
856
        return RemoteStreamSource(self, to_format)
1150
857
 
1151
858
    @needs_read_lock
1152
 
    def get_file_graph(self):
1153
 
        return graph.Graph(self.texts)
1154
 
 
1155
 
    @needs_read_lock
1156
859
    def has_revision(self, revision_id):
1157
860
        """True if this repository has a copy of the revision."""
1158
861
        # Copy of bzrlib.repository.Repository.has_revision
1175
878
    def _has_same_fallbacks(self, other_repo):
1176
879
        """Returns true if the repositories have the same fallbacks."""
1177
880
        # XXX: copied from Repository; it should be unified into a base class
1178
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
881
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
1179
882
        my_fb = self._fallback_repositories
1180
883
        other_fb = other_repo._fallback_repositories
1181
884
        if len(my_fb) != len(other_fb):
1197
900
        parents_provider = self._make_parents_provider(other_repository)
1198
901
        return graph.Graph(parents_provider)
1199
902
 
1200
 
    @needs_read_lock
1201
 
    def get_known_graph_ancestry(self, revision_ids):
1202
 
        """Return the known graph for a set of revision ids and their ancestors.
1203
 
        """
1204
 
        st = static_tuple.StaticTuple
1205
 
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
1206
 
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
1207
 
        return graph.GraphThunkIdsToKeys(known_graph)
1208
 
 
1209
903
    def gather_stats(self, revid=None, committers=None):
1210
904
        """See Repository.gather_stats()."""
1211
905
        path = self.bzrdir._path_for_remote_call(self._client)
1212
906
        # revid can be None to indicate no revisions, not just NULL_REVISION
1213
 
        if revid is None or _mod_revision.is_null(revid):
 
907
        if revid is None or revision.is_null(revid):
1214
908
            fmt_revid = ''
1215
909
        else:
1216
910
            fmt_revid = revid
1277
971
        pass
1278
972
 
1279
973
    def lock_read(self):
1280
 
        """Lock the repository for read operations.
1281
 
 
1282
 
        :return: A bzrlib.lock.LogicalLockResult.
1283
 
        """
1284
974
        # wrong eventually - want a local lock cache context
1285
975
        if not self._lock_mode:
1286
976
            self._note_lock('r')
1293
983
                repo.lock_read()
1294
984
        else:
1295
985
            self._lock_count += 1
1296
 
        return lock.LogicalLockResult(self.unlock)
1297
986
 
1298
987
    def _remote_lock_write(self, token):
1299
988
        path = self.bzrdir._path_for_remote_call(self._client)
1339
1028
            raise errors.ReadOnlyError(self)
1340
1029
        else:
1341
1030
            self._lock_count += 1
1342
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
1031
        return self._lock_token or None
1343
1032
 
1344
1033
    def leave_lock_in_place(self):
1345
1034
        if not self._lock_token:
1499
1188
 
1500
1189
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1501
1190
                           timezone=None, committer=None, revprops=None,
1502
 
                           revision_id=None, lossy=False):
 
1191
                           revision_id=None):
1503
1192
        # FIXME: It ought to be possible to call this without immediately
1504
1193
        # triggering _ensure_real.  For now it's the easiest thing to do.
1505
1194
        self._ensure_real()
1506
1195
        real_repo = self._real_repository
1507
1196
        builder = real_repo.get_commit_builder(branch, parents,
1508
1197
                config, timestamp=timestamp, timezone=timezone,
1509
 
                committer=committer, revprops=revprops,
1510
 
                revision_id=revision_id, lossy=lossy)
 
1198
                committer=committer, revprops=revprops, revision_id=revision_id)
1511
1199
        return builder
1512
1200
 
1513
1201
    def add_fallback_repository(self, repository):
1521
1209
        # We need to accumulate additional repositories here, to pass them in
1522
1210
        # on various RPC's.
1523
1211
        #
1524
 
        # Make the check before we lock: this raises an exception.
1525
 
        self._check_fallback_repository(repository)
1526
1212
        if self.is_locked():
1527
1213
            # We will call fallback.unlock() when we transition to the unlocked
1528
1214
            # state, so always add a lock here. If a caller passes us a locked
1533
1219
        # _real_branch had its get_stacked_on_url method called), then the
1534
1220
        # repository to be added may already be in the _real_repositories list.
1535
1221
        if self._real_repository is not None:
1536
 
            fallback_locations = [repo.user_url for repo in
 
1222
            fallback_locations = [repo.bzrdir.root_transport.base for repo in
1537
1223
                self._real_repository._fallback_repositories]
1538
 
            if repository.user_url not in fallback_locations:
 
1224
            if repository.bzrdir.root_transport.base not in fallback_locations:
1539
1225
                self._real_repository.add_fallback_repository(repository)
1540
1226
 
1541
 
    def _check_fallback_repository(self, repository):
1542
 
        """Check that this repository can fallback to repository safely.
1543
 
 
1544
 
        Raise an error if not.
1545
 
 
1546
 
        :param repository: A repository to fallback to.
1547
 
        """
1548
 
        return _mod_repository.InterRepository._assert_same_model(
1549
 
            self, repository)
1550
 
 
1551
1227
    def add_inventory(self, revid, inv, parents):
1552
1228
        self._ensure_real()
1553
1229
        return self._real_repository.add_inventory(revid, inv, parents)
1554
1230
 
1555
1231
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1556
 
            parents, basis_inv=None, propagate_caches=False):
 
1232
                               parents):
1557
1233
        self._ensure_real()
1558
1234
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
1559
 
            delta, new_revision_id, parents, basis_inv=basis_inv,
1560
 
            propagate_caches=propagate_caches)
 
1235
            delta, new_revision_id, parents)
1561
1236
 
1562
1237
    def add_revision(self, rev_id, rev, inv=None, config=None):
1563
1238
        self._ensure_real()
1593
1268
        return self._real_repository.make_working_trees()
1594
1269
 
1595
1270
    def refresh_data(self):
1596
 
        """Re-read any data needed to synchronise with disk.
 
1271
        """Re-read any data needed to to synchronise with disk.
1597
1272
 
1598
1273
        This method is intended to be called after another repository instance
1599
1274
        (such as one used by a smart server) has inserted data into the
1600
 
        repository. On all repositories this will work outside of write groups.
1601
 
        Some repository formats (pack and newer for bzrlib native formats)
1602
 
        support refresh_data inside write groups. If called inside a write
1603
 
        group on a repository that does not support refreshing in a write group
1604
 
        IsInWriteGroupError will be raised.
 
1275
        repository. It may not be called during a write group, but may be
 
1276
        called at any other time.
1605
1277
        """
 
1278
        if self.is_in_write_group():
 
1279
            raise errors.InternalBzrError(
 
1280
                "May not refresh_data while in a write group.")
1606
1281
        if self._real_repository is not None:
1607
1282
            self._real_repository.refresh_data()
1608
1283
 
1620
1295
        return result
1621
1296
 
1622
1297
    @needs_read_lock
1623
 
    def search_missing_revision_ids(self, other,
1624
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
1625
 
            find_ghosts=True, revision_ids=None, if_present_ids=None,
1626
 
            limit=None):
 
1298
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1627
1299
        """Return the revision ids that other has that this does not.
1628
1300
 
1629
1301
        These are returned in topological order.
1630
1302
 
1631
1303
        revision_id: only return revision ids included by revision_id.
1632
1304
        """
1633
 
        if symbol_versioning.deprecated_passed(revision_id):
1634
 
            symbol_versioning.warn(
1635
 
                'search_missing_revision_ids(revision_id=...) was '
1636
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
1637
 
                DeprecationWarning, stacklevel=2)
1638
 
            if revision_ids is not None:
1639
 
                raise AssertionError(
1640
 
                    'revision_ids is mutually exclusive with revision_id')
1641
 
            if revision_id is not None:
1642
 
                revision_ids = [revision_id]
1643
 
        inter_repo = _mod_repository.InterRepository.get(other, self)
1644
 
        return inter_repo.search_missing_revision_ids(
1645
 
            find_ghosts=find_ghosts, revision_ids=revision_ids,
1646
 
            if_present_ids=if_present_ids, limit=limit)
 
1305
        return repository.InterRepository.get(
 
1306
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
1647
1307
 
1648
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1308
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
1649
1309
            fetch_spec=None):
1650
1310
        # No base implementation to use as RemoteRepository is not a subclass
1651
1311
        # of Repository; so this is a copy of Repository.fetch().
1662
1322
            # check that last_revision is in 'from' and then return a
1663
1323
            # no-operation.
1664
1324
            if (revision_id is not None and
1665
 
                not _mod_revision.is_null(revision_id)):
 
1325
                not revision.is_null(revision_id)):
1666
1326
                self.get_revision(revision_id)
1667
1327
            return 0, []
1668
1328
        # if there is no specific appropriate InterRepository, this will get
1669
1329
        # the InterRepository base class, which raises an
1670
1330
        # IncompatibleRepositories when asked to fetch.
1671
 
        inter = _mod_repository.InterRepository.get(source, self)
1672
 
        return inter.fetch(revision_id=revision_id,
 
1331
        inter = repository.InterRepository.get(source, self)
 
1332
        return inter.fetch(revision_id=revision_id, pb=pb,
1673
1333
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1674
1334
 
1675
1335
    def create_bundle(self, target, base, fileobj, format=None):
1677
1337
        self._real_repository.create_bundle(target, base, fileobj, format)
1678
1338
 
1679
1339
    @needs_read_lock
1680
 
    @symbol_versioning.deprecated_method(
1681
 
        symbol_versioning.deprecated_in((2, 4, 0)))
1682
1340
    def get_ancestry(self, revision_id, topo_sorted=True):
1683
1341
        self._ensure_real()
1684
1342
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
1698
1356
        self._ensure_real()
1699
1357
        return self._real_repository.iter_files_bytes(desired_files)
1700
1358
 
1701
 
    def get_cached_parent_map(self, revision_ids):
1702
 
        """See bzrlib.CachingParentsProvider.get_cached_parent_map"""
1703
 
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
1704
 
 
1705
1359
    def get_parent_map(self, revision_ids):
1706
1360
        """See bzrlib.Graph.get_parent_map()."""
1707
1361
        return self._make_parents_provider().get_parent_map(revision_ids)
1765
1419
        if parents_map is None:
1766
1420
            # Repository is not locked, so there's no cache.
1767
1421
            parents_map = {}
1768
 
        if _DEFAULT_SEARCH_DEPTH <= 0:
1769
 
            (start_set, stop_keys,
1770
 
             key_count) = graph.search_result_from_parent_map(
1771
 
                parents_map, self._unstacked_provider.missing_keys)
1772
 
        else:
1773
 
            (start_set, stop_keys,
1774
 
             key_count) = graph.limited_search_result_from_parent_map(
1775
 
                parents_map, self._unstacked_provider.missing_keys,
1776
 
                keys, depth=_DEFAULT_SEARCH_DEPTH)
 
1422
        # start_set is all the keys in the cache
 
1423
        start_set = set(parents_map)
 
1424
        # result set is all the references to keys in the cache
 
1425
        result_parents = set()
 
1426
        for parents in parents_map.itervalues():
 
1427
            result_parents.update(parents)
 
1428
        stop_keys = result_parents.difference(start_set)
 
1429
        # We don't need to send ghosts back to the server as a position to
 
1430
        # stop either.
 
1431
        stop_keys.difference_update(self._unstacked_provider.missing_keys)
 
1432
        key_count = len(parents_map)
 
1433
        if (NULL_REVISION in result_parents
 
1434
            and NULL_REVISION in self._unstacked_provider.missing_keys):
 
1435
            # If we pruned NULL_REVISION from the stop_keys because it's also
 
1436
            # in our cache of "missing" keys we need to increment our key count
 
1437
            # by 1, because the reconsitituted SearchResult on the server will
 
1438
            # still consider NULL_REVISION to be an included key.
 
1439
            key_count += 1
 
1440
        included_keys = start_set.intersection(result_parents)
 
1441
        start_set.difference_update(included_keys)
1777
1442
        recipe = ('manual', start_set, stop_keys, key_count)
1778
1443
        body = self._serialise_search_recipe(recipe)
1779
1444
        path = self.bzrdir._path_for_remote_call(self._client)
1836
1501
        self._ensure_real()
1837
1502
        return self._real_repository._get_inventory_xml(revision_id)
1838
1503
 
 
1504
    def _deserialise_inventory(self, revision_id, xml):
 
1505
        self._ensure_real()
 
1506
        return self._real_repository._deserialise_inventory(revision_id, xml)
 
1507
 
1839
1508
    def reconcile(self, other=None, thorough=False):
1840
1509
        self._ensure_real()
1841
1510
        return self._real_repository.reconcile(other=other, thorough=thorough)
1883
1552
        from bzrlib import osutils
1884
1553
        import tarfile
1885
1554
        # TODO: Maybe a progress bar while streaming the tarball?
1886
 
        note(gettext("Copying repository content as tarball..."))
 
1555
        note("Copying repository content as tarball...")
1887
1556
        tar_file = self._get_tarball('bz2')
1888
1557
        if tar_file is None:
1889
1558
            return None
1894
1563
            tmpdir = osutils.mkdtemp()
1895
1564
            try:
1896
1565
                _extract_tar(tar, tmpdir)
1897
 
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
 
1566
                tmp_bzrdir = BzrDir.open(tmpdir)
1898
1567
                tmp_repo = tmp_bzrdir.open_repository()
1899
1568
                tmp_repo.copy_content_into(destination, revision_id)
1900
1569
            finally:
1916
1585
        return self._real_repository.inventories
1917
1586
 
1918
1587
    @needs_write_lock
1919
 
    def pack(self, hint=None, clean_obsolete_packs=False):
 
1588
    def pack(self, hint=None):
1920
1589
        """Compress the data within the repository.
1921
1590
 
1922
1591
        This is not currently implemented within the smart server.
1923
1592
        """
1924
1593
        self._ensure_real()
1925
 
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
 
1594
        return self._real_repository.pack(hint=hint)
1926
1595
 
1927
1596
    @property
1928
1597
    def revisions(self):
1985
1654
    def supports_rich_root(self):
1986
1655
        return self._format.rich_root_data
1987
1656
 
1988
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1989
1657
    def iter_reverse_revision_history(self, revision_id):
1990
1658
        self._ensure_real()
1991
1659
        return self._real_repository.iter_reverse_revision_history(revision_id)
2012
1680
        return self._real_repository.item_keys_introduced_by(revision_ids,
2013
1681
            _files_pb=_files_pb)
2014
1682
 
 
1683
    def revision_graph_can_have_wrong_parents(self):
 
1684
        # The answer depends on the remote repo format.
 
1685
        self._ensure_real()
 
1686
        return self._real_repository.revision_graph_can_have_wrong_parents()
 
1687
 
2015
1688
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2016
1689
        self._ensure_real()
2017
1690
        return self._real_repository._find_inconsistent_revision_parents(
2025
1698
        providers = [self._unstacked_provider]
2026
1699
        if other is not None:
2027
1700
            providers.insert(0, other)
2028
 
        return graph.StackedParentsProvider(_LazyListJoin(
2029
 
            providers, self._fallback_repositories))
 
1701
        providers.extend(r._make_parents_provider() for r in
 
1702
                         self._fallback_repositories)
 
1703
        return graph.StackedParentsProvider(providers)
2030
1704
 
2031
1705
    def _serialise_search_recipe(self, recipe):
2032
1706
        """Serialise a graph search recipe.
2040
1714
        return '\n'.join((start_keys, stop_keys, count))
2041
1715
 
2042
1716
    def _serialise_search_result(self, search_result):
2043
 
        parts = search_result.get_network_struct()
 
1717
        if isinstance(search_result, graph.PendingAncestryResult):
 
1718
            parts = ['ancestry-of']
 
1719
            parts.extend(search_result.heads)
 
1720
        else:
 
1721
            recipe = search_result.get_recipe()
 
1722
            parts = [recipe[0], self._serialise_search_recipe(recipe)]
2044
1723
        return '\n'.join(parts)
2045
1724
 
2046
1725
    def autopack(self):
2056
1735
            raise errors.UnexpectedSmartServerResponse(response)
2057
1736
 
2058
1737
 
2059
 
class RemoteStreamSink(vf_repository.StreamSink):
 
1738
class RemoteStreamSink(repository.StreamSink):
2060
1739
 
2061
1740
    def _insert_real(self, stream, src_format, resume_tokens):
2062
1741
        self.target_repo._ensure_real()
2163
1842
        self._last_substream and self._last_stream so that the stream can be
2164
1843
        resumed by _resume_stream_with_vfs.
2165
1844
        """
2166
 
 
 
1845
                    
2167
1846
        stream_iter = iter(stream)
2168
1847
        for substream_kind, substream in stream_iter:
2169
1848
            if substream_kind == 'inventory-deltas':
2172
1851
                return
2173
1852
            else:
2174
1853
                yield substream_kind, substream
2175
 
 
2176
 
 
2177
 
class RemoteStreamSource(vf_repository.StreamSource):
 
1854
            
 
1855
 
 
1856
class RemoteStreamSource(repository.StreamSource):
2178
1857
    """Stream data from a remote server."""
2179
1858
 
2180
1859
    def get_stream(self, search):
2240
1919
        candidate_verbs = [
2241
1920
            ('Repository.get_stream_1.19', (1, 19)),
2242
1921
            ('Repository.get_stream', (1, 13))]
2243
 
 
2244
1922
        found_verb = False
2245
1923
        for verb, version in candidate_verbs:
2246
1924
            if medium._is_remote_before(version):
2250
1928
                    verb, args, search_bytes)
2251
1929
            except errors.UnknownSmartMethod:
2252
1930
                medium._remember_remote_is_before(version)
2253
 
            except errors.UnknownErrorFromSmartServer, e:
2254
 
                if isinstance(search, graph.EverythingResult):
2255
 
                    error_verb = e.error_from_smart_server.error_verb
2256
 
                    if error_verb == 'BadSearch':
2257
 
                        # Pre-2.4 servers don't support this sort of search.
2258
 
                        # XXX: perhaps falling back to VFS on BadSearch is a
2259
 
                        # good idea in general?  It might provide a little bit
2260
 
                        # of protection against client-side bugs.
2261
 
                        medium._remember_remote_is_before((2, 4))
2262
 
                        break
2263
 
                raise
2264
1931
            else:
2265
1932
                response_tuple, response_handler = response
2266
1933
                found_verb = True
2270
1937
        if response_tuple[0] != 'ok':
2271
1938
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2272
1939
        byte_stream = response_handler.read_streamed_body()
2273
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
2274
 
            self._record_counter)
 
1940
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
2275
1941
        if src_format.network_name() != repo._format.network_name():
2276
1942
            raise AssertionError(
2277
1943
                "Mismatched RemoteRepository and stream src %r, %r" % (
2359
2025
    def network_name(self):
2360
2026
        return self._network_name
2361
2027
 
2362
 
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2363
 
        return a_bzrdir.open_branch(name=name, 
2364
 
            ignore_fallbacks=ignore_fallbacks)
 
2028
    def open(self, a_bzrdir, ignore_fallbacks=False):
 
2029
        return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2365
2030
 
2366
 
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
 
2031
    def _vfs_initialize(self, a_bzrdir):
2367
2032
        # Initialisation when using a local bzrdir object, or a non-vfs init
2368
2033
        # method is not available on the server.
2369
2034
        # self._custom_format is always set - the start of initialize ensures
2370
2035
        # that.
2371
2036
        if isinstance(a_bzrdir, RemoteBzrDir):
2372
2037
            a_bzrdir._ensure_real()
2373
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2374
 
                name, append_revisions_only=append_revisions_only)
 
2038
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
2375
2039
        else:
2376
2040
            # We assume the bzrdir is parameterised; it may not be.
2377
 
            result = self._custom_format.initialize(a_bzrdir, name,
2378
 
                append_revisions_only=append_revisions_only)
 
2041
            result = self._custom_format.initialize(a_bzrdir)
2379
2042
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2380
2043
            not isinstance(result, RemoteBranch)):
2381
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2382
 
                                  name=name)
 
2044
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
2383
2045
        return result
2384
2046
 
2385
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2386
 
                   append_revisions_only=None):
 
2047
    def initialize(self, a_bzrdir):
2387
2048
        # 1) get the network name to use.
2388
2049
        if self._custom_format:
2389
2050
            network_name = self._custom_format.network_name()
2390
2051
        else:
2391
2052
            # Select the current bzrlib default and ask for that.
2392
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
2053
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
2393
2054
            reference_format = reference_bzrdir_format.get_branch_format()
2394
2055
            self._custom_format = reference_format
2395
2056
            network_name = reference_format.network_name()
2396
2057
        # Being asked to create on a non RemoteBzrDir:
2397
2058
        if not isinstance(a_bzrdir, RemoteBzrDir):
2398
 
            return self._vfs_initialize(a_bzrdir, name=name,
2399
 
                append_revisions_only=append_revisions_only)
 
2059
            return self._vfs_initialize(a_bzrdir)
2400
2060
        medium = a_bzrdir._client._medium
2401
2061
        if medium._is_remote_before((1, 13)):
2402
 
            return self._vfs_initialize(a_bzrdir, name=name,
2403
 
                append_revisions_only=append_revisions_only)
 
2062
            return self._vfs_initialize(a_bzrdir)
2404
2063
        # Creating on a remote bzr dir.
2405
2064
        # 2) try direct creation via RPC
2406
2065
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2407
 
        if name is not None:
2408
 
            # XXX JRV20100304: Support creating colocated branches
2409
 
            raise errors.NoColocatedBranchSupport(self)
2410
2066
        verb = 'BzrDir.create_branch'
2411
2067
        try:
2412
2068
            response = a_bzrdir._call(verb, path, network_name)
2413
2069
        except errors.UnknownSmartMethod:
2414
2070
            # Fallback - use vfs methods
2415
2071
            medium._remember_remote_is_before((1, 13))
2416
 
            return self._vfs_initialize(a_bzrdir, name=name,
2417
 
                    append_revisions_only=append_revisions_only)
 
2072
            return self._vfs_initialize(a_bzrdir)
2418
2073
        if response[0] != 'ok':
2419
2074
            raise errors.UnexpectedSmartServerResponse(response)
2420
2075
        # Turn the response into a RemoteRepository object.
2421
2076
        format = RemoteBranchFormat(network_name=response[1])
2422
2077
        repo_format = response_tuple_to_repo_format(response[3:])
2423
 
        repo_path = response[2]
2424
 
        if repository is not None:
2425
 
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
2426
 
            url_diff = urlutils.relative_url(repository.user_url,
2427
 
                    remote_repo_url)
2428
 
            if url_diff != '.':
2429
 
                raise AssertionError(
2430
 
                    'repository.user_url %r does not match URL from server '
2431
 
                    'response (%r + %r)'
2432
 
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
2433
 
            remote_repo = repository
 
2078
        if response[2] == '':
 
2079
            repo_bzrdir = a_bzrdir
2434
2080
        else:
2435
 
            if repo_path == '':
2436
 
                repo_bzrdir = a_bzrdir
2437
 
            else:
2438
 
                repo_bzrdir = RemoteBzrDir(
2439
 
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
2440
 
                    a_bzrdir._client)
2441
 
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
2081
            repo_bzrdir = RemoteBzrDir(
 
2082
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
 
2083
                a_bzrdir._client)
 
2084
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2442
2085
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2443
 
            format=format, setup_stacking=False, name=name)
2444
 
        if append_revisions_only:
2445
 
            remote_branch.set_append_revisions_only(append_revisions_only)
 
2086
            format=format, setup_stacking=False)
2446
2087
        # XXX: We know this is a new branch, so it must have revno 0, revid
2447
2088
        # NULL_REVISION. Creating the branch locked would make this be unable
2448
2089
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2467
2108
        self._ensure_real()
2468
2109
        return self._custom_format.supports_set_append_revisions_only()
2469
2110
 
2470
 
    def _use_default_local_heads_to_fetch(self):
2471
 
        # If the branch format is a metadir format *and* its heads_to_fetch
2472
 
        # implementation is not overridden vs the base class, we can use the
2473
 
        # base class logic rather than use the heads_to_fetch RPC.  This is
2474
 
        # usually cheaper in terms of net round trips, as the last-revision and
2475
 
        # tags info fetched is cached and would be fetched anyway.
2476
 
        self._ensure_real()
2477
 
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
2478
 
            branch_class = self._custom_format._branch_class()
2479
 
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
2480
 
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
2481
 
                return True
2482
 
        return False
2483
2111
 
2484
2112
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2485
2113
    """Branch stored on a server accessed by HPSS RPC.
2488
2116
    """
2489
2117
 
2490
2118
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2491
 
        _client=None, format=None, setup_stacking=True, name=None):
 
2119
        _client=None, format=None, setup_stacking=True):
2492
2120
        """Create a RemoteBranch instance.
2493
2121
 
2494
2122
        :param real_branch: An optional local implementation of the branch
2500
2128
        :param setup_stacking: If True make an RPC call to determine the
2501
2129
            stacked (or not) status of the branch. If False assume the branch
2502
2130
            is not stacked.
2503
 
        :param name: Colocated branch name
2504
2131
        """
2505
2132
        # We intentionally don't call the parent class's __init__, because it
2506
2133
        # will try to assign to self.tags, which is a property in this subclass.
2525
2152
            self._real_branch = None
2526
2153
        # Fill out expected attributes of branch for bzrlib API users.
2527
2154
        self._clear_cached_state()
2528
 
        # TODO: deprecate self.base in favor of user_url
2529
 
        self.base = self.bzrdir.user_url
2530
 
        self._name = name
 
2155
        self.base = self.bzrdir.root_transport.base
2531
2156
        self._control_files = None
2532
2157
        self._lock_mode = None
2533
2158
        self._lock_token = None
2598
2223
                    'to use vfs implementation')
2599
2224
            self.bzrdir._ensure_real()
2600
2225
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
2601
 
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
 
2226
                ignore_fallbacks=self._real_ignore_fallbacks)
2602
2227
            if self.repository._real_repository is None:
2603
2228
                # Give the remote repository the matching real repo.
2604
2229
                real_repo = self._real_branch.repository
2643
2268
                self.bzrdir, self._client)
2644
2269
        return self._control_files
2645
2270
 
2646
 
    def _get_checkout_format(self, lightweight=False):
 
2271
    def _get_checkout_format(self):
2647
2272
        self._ensure_real()
2648
 
        if lightweight:
2649
 
            format = RemoteBzrDirFormat()
2650
 
            self.bzrdir._format._supply_sub_formats_to(format)
2651
 
            format.workingtree_format = self._real_branch._get_checkout_format(
2652
 
                lightweight=lightweight).workingtree_format
2653
 
            return format
2654
 
        else:
2655
 
            return self._real_branch._get_checkout_format(lightweight=False)
 
2273
        return self._real_branch._get_checkout_format()
2656
2274
 
2657
2275
    def get_physical_lock_status(self):
2658
2276
        """See Branch.get_physical_lock_status()."""
2691
2309
            self._is_stacked = False
2692
2310
        else:
2693
2311
            self._is_stacked = True
2694
 
 
 
2312
        
2695
2313
    def _vfs_get_tags_bytes(self):
2696
2314
        self._ensure_real()
2697
2315
        return self._real_branch._get_tags_bytes()
2698
2316
 
2699
 
    @needs_read_lock
2700
2317
    def _get_tags_bytes(self):
2701
 
        if self._tags_bytes is None:
2702
 
            self._tags_bytes = self._get_tags_bytes_via_hpss()
2703
 
        return self._tags_bytes
2704
 
 
2705
 
    def _get_tags_bytes_via_hpss(self):
2706
2318
        medium = self._client._medium
2707
2319
        if medium._is_remote_before((1, 13)):
2708
2320
            return self._vfs_get_tags_bytes()
2718
2330
        return self._real_branch._set_tags_bytes(bytes)
2719
2331
 
2720
2332
    def _set_tags_bytes(self, bytes):
2721
 
        if self.is_locked():
2722
 
            self._tags_bytes = bytes
2723
2333
        medium = self._client._medium
2724
2334
        if medium._is_remote_before((1, 18)):
2725
2335
            self._vfs_set_tags_bytes(bytes)
2734
2344
            self._vfs_set_tags_bytes(bytes)
2735
2345
 
2736
2346
    def lock_read(self):
2737
 
        """Lock the branch for read operations.
2738
 
 
2739
 
        :return: A bzrlib.lock.LogicalLockResult.
2740
 
        """
2741
2347
        self.repository.lock_read()
2742
2348
        if not self._lock_mode:
2743
2349
            self._note_lock('r')
2747
2353
                self._real_branch.lock_read()
2748
2354
        else:
2749
2355
            self._lock_count += 1
2750
 
        return lock.LogicalLockResult(self.unlock)
2751
2356
 
2752
2357
    def _remote_lock_write(self, token):
2753
2358
        if token is None:
2754
2359
            branch_token = repo_token = ''
2755
2360
        else:
2756
2361
            branch_token = token
2757
 
            repo_token = self.repository.lock_write().repository_token
 
2362
            repo_token = self.repository.lock_write()
2758
2363
            self.repository.unlock()
2759
2364
        err_context = {'token': token}
2760
 
        try:
2761
 
            response = self._call(
2762
 
                'Branch.lock_write', self._remote_path(), branch_token,
2763
 
                repo_token or '', **err_context)
2764
 
        except errors.LockContention, e:
2765
 
            # The LockContention from the server doesn't have any
2766
 
            # information about the lock_url. We re-raise LockContention
2767
 
            # with valid lock_url.
2768
 
            raise errors.LockContention('(remote lock)',
2769
 
                self.repository.base.split('.bzr/')[0])
 
2365
        response = self._call(
 
2366
            'Branch.lock_write', self._remote_path(), branch_token,
 
2367
            repo_token or '', **err_context)
2770
2368
        if response[0] != 'ok':
2771
2369
            raise errors.UnexpectedSmartServerResponse(response)
2772
2370
        ok, branch_token, repo_token = response
2793
2391
            self._lock_mode = 'w'
2794
2392
            self._lock_count = 1
2795
2393
        elif self._lock_mode == 'r':
2796
 
            raise errors.ReadOnlyError(self)
 
2394
            raise errors.ReadOnlyTransaction
2797
2395
        else:
2798
2396
            if token is not None:
2799
2397
                # A token was given to lock_write, and we're relocking, so
2804
2402
            self._lock_count += 1
2805
2403
            # Re-lock the repository too.
2806
2404
            self.repository.lock_write(self._repo_lock_token)
2807
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2405
        return self._lock_token or None
2808
2406
 
2809
2407
    def _unlock(self, branch_token, repo_token):
2810
2408
        err_context = {'token': str((branch_token, repo_token))}
2879
2477
            missing_parent = parent_map[missing_parent]
2880
2478
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2881
2479
 
2882
 
    def _read_last_revision_info(self):
 
2480
    def _last_revision_info(self):
2883
2481
        response = self._call('Branch.last_revision_info', self._remote_path())
2884
2482
        if response[0] != 'ok':
2885
2483
            raise SmartProtocolError('unexpected response code %s' % (response,))
2948
2546
            raise errors.UnexpectedSmartServerResponse(response)
2949
2547
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2950
2548
 
2951
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2952
2549
    @needs_write_lock
2953
2550
    def set_revision_history(self, rev_history):
2954
 
        """See Branch.set_revision_history."""
2955
 
        self._set_revision_history(rev_history)
2956
 
 
2957
 
    @needs_write_lock
2958
 
    def _set_revision_history(self, rev_history):
2959
2551
        # Send just the tip revision of the history; the server will generate
2960
2552
        # the full history from that.  If the revision doesn't exist in this
2961
2553
        # branch, NoSuchRevision will be raised.
3019
2611
            _override_hook_target=self, **kwargs)
3020
2612
 
3021
2613
    @needs_read_lock
3022
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
 
2614
    def push(self, target, overwrite=False, stop_revision=None):
3023
2615
        self._ensure_real()
3024
2616
        return self._real_branch.push(
3025
 
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
 
2617
            target, overwrite=overwrite, stop_revision=stop_revision,
3026
2618
            _override_hook_source_branch=self)
3027
2619
 
3028
2620
    def is_locked(self):
3038
2630
        # XXX: These should be returned by the set_last_revision_info verb
3039
2631
        old_revno, old_revid = self.last_revision_info()
3040
2632
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
3041
 
        if not revision_id or not isinstance(revision_id, basestring):
3042
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2633
        revision_id = ensure_null(revision_id)
3043
2634
        try:
3044
2635
            response = self._call('Branch.set_last_revision_info',
3045
2636
                self._remote_path(), self._lock_token, self._repo_lock_token,
3074
2665
            except errors.UnknownSmartMethod:
3075
2666
                medium._remember_remote_is_before((1, 6))
3076
2667
        self._clear_cached_state_of_remote_branch_only()
3077
 
        self._set_revision_history(self._lefthand_history(revision_id,
 
2668
        self.set_revision_history(self._lefthand_history(revision_id,
3078
2669
            last_rev=last_rev,other_branch=other_branch))
3079
2670
 
3080
2671
    def set_push_location(self, location):
3081
2672
        self._ensure_real()
3082
2673
        return self._real_branch.set_push_location(location)
3083
2674
 
3084
 
    def heads_to_fetch(self):
3085
 
        if self._format._use_default_local_heads_to_fetch():
3086
 
            # We recognise this format, and its heads-to-fetch implementation
3087
 
            # is the default one (tip + tags).  In this case it's cheaper to
3088
 
            # just use the default implementation rather than a special RPC as
3089
 
            # the tip and tags data is cached.
3090
 
            return branch.Branch.heads_to_fetch(self)
3091
 
        medium = self._client._medium
3092
 
        if medium._is_remote_before((2, 4)):
3093
 
            return self._vfs_heads_to_fetch()
3094
 
        try:
3095
 
            return self._rpc_heads_to_fetch()
3096
 
        except errors.UnknownSmartMethod:
3097
 
            medium._remember_remote_is_before((2, 4))
3098
 
            return self._vfs_heads_to_fetch()
3099
 
 
3100
 
    def _rpc_heads_to_fetch(self):
3101
 
        response = self._call('Branch.heads_to_fetch', self._remote_path())
3102
 
        if len(response) != 2:
3103
 
            raise errors.UnexpectedSmartServerResponse(response)
3104
 
        must_fetch, if_present_fetch = response
3105
 
        return set(must_fetch), set(if_present_fetch)
3106
 
 
3107
 
    def _vfs_heads_to_fetch(self):
3108
 
        self._ensure_real()
3109
 
        return self._real_branch.heads_to_fetch()
3110
 
 
3111
2675
 
3112
2676
class RemoteConfig(object):
3113
2677
    """A Config that reads and writes from smart verbs.
3127
2691
        """
3128
2692
        try:
3129
2693
            configobj = self._get_configobj()
3130
 
            section_obj = None
3131
2694
            if section is None:
3132
2695
                section_obj = configobj
3133
2696
            else:
3134
2697
                try:
3135
2698
                    section_obj = configobj[section]
3136
2699
                except KeyError:
3137
 
                    pass
3138
 
            if section_obj is None:
3139
 
                value = default
3140
 
            else:
3141
 
                value = section_obj.get(name, default)
 
2700
                    return default
 
2701
            return section_obj.get(name, default)
3142
2702
        except errors.UnknownSmartMethod:
3143
 
            value = self._vfs_get_option(name, section, default)
3144
 
        for hook in config.OldConfigHooks['get']:
3145
 
            hook(self, name, value)
3146
 
        return value
 
2703
            return self._vfs_get_option(name, section, default)
3147
2704
 
3148
2705
    def _response_to_configobj(self, response):
3149
2706
        if len(response[0]) and response[0][0] != 'ok':
3150
2707
            raise errors.UnexpectedSmartServerResponse(response)
3151
2708
        lines = response[1].read_body_bytes().splitlines()
3152
 
        conf = config.ConfigObj(lines, encoding='utf-8')
3153
 
        for hook in config.OldConfigHooks['load']:
3154
 
            hook(self)
3155
 
        return conf
 
2709
        return config.ConfigObj(lines, encoding='utf-8')
3156
2710
 
3157
2711
 
3158
2712
class RemoteBranchConfig(RemoteConfig):
3177
2731
        medium = self._branch._client._medium
3178
2732
        if medium._is_remote_before((1, 14)):
3179
2733
            return self._vfs_set_option(value, name, section)
3180
 
        if isinstance(value, dict):
3181
 
            if medium._is_remote_before((2, 2)):
3182
 
                return self._vfs_set_option(value, name, section)
3183
 
            return self._set_config_option_dict(value, name, section)
3184
 
        else:
3185
 
            return self._set_config_option(value, name, section)
3186
 
 
3187
 
    def _set_config_option(self, value, name, section):
3188
2734
        try:
3189
2735
            path = self._branch._remote_path()
3190
2736
            response = self._branch._client.call('Branch.set_config_option',
3191
2737
                path, self._branch._lock_token, self._branch._repo_lock_token,
3192
2738
                value.encode('utf8'), name, section or '')
3193
2739
        except errors.UnknownSmartMethod:
3194
 
            medium = self._branch._client._medium
3195
2740
            medium._remember_remote_is_before((1, 14))
3196
2741
            return self._vfs_set_option(value, name, section)
3197
2742
        if response != ():
3198
2743
            raise errors.UnexpectedSmartServerResponse(response)
3199
2744
 
3200
 
    def _serialize_option_dict(self, option_dict):
3201
 
        utf8_dict = {}
3202
 
        for key, value in option_dict.items():
3203
 
            if isinstance(key, unicode):
3204
 
                key = key.encode('utf8')
3205
 
            if isinstance(value, unicode):
3206
 
                value = value.encode('utf8')
3207
 
            utf8_dict[key] = value
3208
 
        return bencode.bencode(utf8_dict)
3209
 
 
3210
 
    def _set_config_option_dict(self, value, name, section):
3211
 
        try:
3212
 
            path = self._branch._remote_path()
3213
 
            serialised_dict = self._serialize_option_dict(value)
3214
 
            response = self._branch._client.call(
3215
 
                'Branch.set_config_option_dict',
3216
 
                path, self._branch._lock_token, self._branch._repo_lock_token,
3217
 
                serialised_dict, name, section or '')
3218
 
        except errors.UnknownSmartMethod:
3219
 
            medium = self._branch._client._medium
3220
 
            medium._remember_remote_is_before((2, 2))
3221
 
            return self._vfs_set_option(value, name, section)
3222
 
        if response != ():
3223
 
            raise errors.UnexpectedSmartServerResponse(response)
3224
 
 
3225
2745
    def _real_object(self):
3226
2746
        self._branch._ensure_real()
3227
2747
        return self._branch._real_branch
3310
2830
                    'Missing key %r in context %r', key_err.args[0], context)
3311
2831
                raise err
3312
2832
 
3313
 
    if err.error_verb == 'NoSuchRevision':
 
2833
    if err.error_verb == 'IncompatibleRepositories':
 
2834
        raise errors.IncompatibleRepositories(err.error_args[0],
 
2835
            err.error_args[1], err.error_args[2])
 
2836
    elif err.error_verb == 'NoSuchRevision':
3314
2837
        raise NoSuchRevision(find('branch'), err.error_args[0])
3315
2838
    elif err.error_verb == 'nosuchrevision':
3316
2839
        raise NoSuchRevision(find('repository'), err.error_args[0])
3323
2846
            detail=extra)
3324
2847
    elif err.error_verb == 'norepository':
3325
2848
        raise errors.NoRepositoryPresent(find('bzrdir'))
 
2849
    elif err.error_verb == 'LockContention':
 
2850
        raise errors.LockContention('(remote lock)')
3326
2851
    elif err.error_verb == 'UnlockableTransport':
3327
2852
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
 
2853
    elif err.error_verb == 'LockFailed':
 
2854
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
3328
2855
    elif err.error_verb == 'TokenMismatch':
3329
2856
        raise errors.TokenMismatch(find('token'), '(remote token)')
3330
2857
    elif err.error_verb == 'Diverged':
3331
2858
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
 
2859
    elif err.error_verb == 'TipChangeRejected':
 
2860
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
 
2861
    elif err.error_verb == 'UnstackableBranchFormat':
 
2862
        raise errors.UnstackableBranchFormat(*err.error_args)
 
2863
    elif err.error_verb == 'UnstackableRepositoryFormat':
 
2864
        raise errors.UnstackableRepositoryFormat(*err.error_args)
3332
2865
    elif err.error_verb == 'NotStacked':
3333
2866
        raise errors.NotStacked(branch=find('branch'))
3334
2867
    elif err.error_verb == 'PermissionDenied':
3344
2877
    elif err.error_verb == 'NoSuchFile':
3345
2878
        path = get_path()
3346
2879
        raise errors.NoSuchFile(path)
3347
 
    _translate_error_without_context(err)
3348
 
 
3349
 
 
3350
 
def _translate_error_without_context(err):
3351
 
    """Translate any ErrorFromSmartServer values that don't require context"""
3352
 
    if err.error_verb == 'IncompatibleRepositories':
3353
 
        raise errors.IncompatibleRepositories(err.error_args[0],
3354
 
            err.error_args[1], err.error_args[2])
3355
 
    elif err.error_verb == 'LockContention':
3356
 
        raise errors.LockContention('(remote lock)')
3357
 
    elif err.error_verb == 'LockFailed':
3358
 
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
3359
 
    elif err.error_verb == 'TipChangeRejected':
3360
 
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
3361
 
    elif err.error_verb == 'UnstackableBranchFormat':
3362
 
        raise errors.UnstackableBranchFormat(*err.error_args)
3363
 
    elif err.error_verb == 'UnstackableRepositoryFormat':
3364
 
        raise errors.UnstackableRepositoryFormat(*err.error_args)
3365
2880
    elif err.error_verb == 'FileExists':
3366
2881
        raise errors.FileExists(err.error_args[0])
3367
2882
    elif err.error_verb == 'DirectoryNotEmpty':
3386
2901
            raise UnicodeEncodeError(encoding, val, start, end, reason)
3387
2902
    elif err.error_verb == 'ReadOnlyError':
3388
2903
        raise errors.TransportNotPossible('readonly transport')
3389
 
    elif err.error_verb == 'MemoryError':
3390
 
        raise errors.BzrError("remote server out of memory\n"
3391
 
            "Retry non-remotely, or contact the server admin for details.")
3392
2904
    raise errors.UnknownErrorFromSmartServer(err)