/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Jelmer Vernooij
  • Date: 2011-11-28 15:18:59 UTC
  • mto: This revision was merged to the branch mainline in revision 6317.
  • Revision ID: jelmer@samba.org-20111128151859-dw4nua1xiobilw4r
Fix tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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,
 
22
    bzrdir as _mod_bzrdir,
23
23
    config,
 
24
    controldir,
24
25
    debug,
25
26
    errors,
 
27
    gpg,
26
28
    graph,
27
29
    lock,
28
30
    lockdir,
29
 
    repository,
 
31
    registry,
30
32
    repository as _mod_repository,
31
 
    revision,
32
33
    revision as _mod_revision,
33
34
    static_tuple,
34
35
    symbol_versioning,
35
 
)
36
 
from bzrlib.branch import BranchReferenceFormat
37
 
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
 
36
    testament as _mod_testament,
 
37
    urlutils,
 
38
    vf_repository,
 
39
    )
 
40
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
38
41
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
39
42
from bzrlib.errors import (
40
43
    NoSuchRevision,
41
44
    SmartProtocolError,
42
45
    )
 
46
from bzrlib.i18n import gettext
 
47
from bzrlib.inventory import Inventory
43
48
from bzrlib.lockable_files import LockableFiles
44
49
from bzrlib.smart import client, vfs, repository as smart_repo
45
 
from bzrlib.revision import ensure_null, NULL_REVISION
46
 
from bzrlib.trace import mutter, note, warning
 
50
from bzrlib.smart.client import _SmartClient
 
51
from bzrlib.revision import NULL_REVISION
 
52
from bzrlib.revisiontree import InventoryRevisionTree
 
53
from bzrlib.repository import RepositoryWriteLockResult, _LazyListJoin
 
54
from bzrlib.trace import mutter, note, warning, log_exception_quietly
 
55
 
 
56
 
 
57
_DEFAULT_SEARCH_DEPTH = 100
47
58
 
48
59
 
49
60
class _RpcHelper(object):
86
97
    return format
87
98
 
88
99
 
89
 
# Note: RemoteBzrDirFormat is in bzrdir.py
90
 
 
91
 
class RemoteBzrDir(BzrDir, _RpcHelper):
 
100
# Note that RemoteBzrDirProber lives in bzrlib.bzrdir so bzrlib.remote
 
101
# does not have to be imported unless a remote format is involved.
 
102
 
 
103
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
 
104
    """Format representing bzrdirs accessed via a smart server"""
 
105
 
 
106
    supports_workingtrees = False
 
107
 
 
108
    def __init__(self):
 
109
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
 
110
        # XXX: It's a bit ugly that the network name is here, because we'd
 
111
        # like to believe that format objects are stateless or at least
 
112
        # immutable,  However, we do at least avoid mutating the name after
 
113
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
 
114
        self._network_name = None
 
115
 
 
116
    def __repr__(self):
 
117
        return "%s(_network_name=%r)" % (self.__class__.__name__,
 
118
            self._network_name)
 
119
 
 
120
    def get_format_description(self):
 
121
        if self._network_name:
 
122
            try:
 
123
                real_format = controldir.network_format_registry.get(
 
124
                        self._network_name)
 
125
            except KeyError:
 
126
                pass
 
127
            else:
 
128
                return 'Remote: ' + real_format.get_format_description()
 
129
        return 'bzr remote bzrdir'
 
130
 
 
131
    def get_format_string(self):
 
132
        raise NotImplementedError(self.get_format_string)
 
133
 
 
134
    def network_name(self):
 
135
        if self._network_name:
 
136
            return self._network_name
 
137
        else:
 
138
            raise AssertionError("No network name set.")
 
139
 
 
140
    def initialize_on_transport(self, transport):
 
141
        try:
 
142
            # hand off the request to the smart server
 
143
            client_medium = transport.get_smart_medium()
 
144
        except errors.NoSmartMedium:
 
145
            # TODO: lookup the local format from a server hint.
 
146
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
147
            return local_dir_format.initialize_on_transport(transport)
 
148
        client = _SmartClient(client_medium)
 
149
        path = client.remote_path_from_transport(transport)
 
150
        try:
 
151
            response = client.call('BzrDirFormat.initialize', path)
 
152
        except errors.ErrorFromSmartServer, err:
 
153
            _translate_error(err, path=path)
 
154
        if response[0] != 'ok':
 
155
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
 
156
        format = RemoteBzrDirFormat()
 
157
        self._supply_sub_formats_to(format)
 
158
        return RemoteBzrDir(transport, format)
 
159
 
 
160
    def parse_NoneTrueFalse(self, arg):
 
161
        if not arg:
 
162
            return None
 
163
        if arg == 'False':
 
164
            return False
 
165
        if arg == 'True':
 
166
            return True
 
167
        raise AssertionError("invalid arg %r" % arg)
 
168
 
 
169
    def _serialize_NoneTrueFalse(self, arg):
 
170
        if arg is False:
 
171
            return 'False'
 
172
        if arg:
 
173
            return 'True'
 
174
        return ''
 
175
 
 
176
    def _serialize_NoneString(self, arg):
 
177
        return arg or ''
 
178
 
 
179
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
180
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
181
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
182
        shared_repo=False):
 
183
        try:
 
184
            # hand off the request to the smart server
 
185
            client_medium = transport.get_smart_medium()
 
186
        except errors.NoSmartMedium:
 
187
            do_vfs = True
 
188
        else:
 
189
            # Decline to open it if the server doesn't support our required
 
190
            # version (3) so that the VFS-based transport will do it.
 
191
            if client_medium.should_probe():
 
192
                try:
 
193
                    server_version = client_medium.protocol_version()
 
194
                    if server_version != '2':
 
195
                        do_vfs = True
 
196
                    else:
 
197
                        do_vfs = False
 
198
                except errors.SmartProtocolError:
 
199
                    # Apparently there's no usable smart server there, even though
 
200
                    # the medium supports the smart protocol.
 
201
                    do_vfs = True
 
202
            else:
 
203
                do_vfs = False
 
204
        if not do_vfs:
 
205
            client = _SmartClient(client_medium)
 
206
            path = client.remote_path_from_transport(transport)
 
207
            if client_medium._is_remote_before((1, 16)):
 
208
                do_vfs = True
 
209
        if do_vfs:
 
210
            # TODO: lookup the local format from a server hint.
 
211
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
212
            self._supply_sub_formats_to(local_dir_format)
 
213
            return local_dir_format.initialize_on_transport_ex(transport,
 
214
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
215
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
216
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
217
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
218
                vfs_only=True)
 
219
        return self._initialize_on_transport_ex_rpc(client, path, transport,
 
220
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
221
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
 
222
 
 
223
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
 
224
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
225
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
 
226
        args = []
 
227
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
 
228
        args.append(self._serialize_NoneTrueFalse(create_prefix))
 
229
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
 
230
        args.append(self._serialize_NoneString(stacked_on))
 
231
        # stack_on_pwd is often/usually our transport
 
232
        if stack_on_pwd:
 
233
            try:
 
234
                stack_on_pwd = transport.relpath(stack_on_pwd)
 
235
                if not stack_on_pwd:
 
236
                    stack_on_pwd = '.'
 
237
            except errors.PathNotChild:
 
238
                pass
 
239
        args.append(self._serialize_NoneString(stack_on_pwd))
 
240
        args.append(self._serialize_NoneString(repo_format_name))
 
241
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
 
242
        args.append(self._serialize_NoneTrueFalse(shared_repo))
 
243
        request_network_name = self._network_name or \
 
244
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
 
245
        try:
 
246
            response = client.call('BzrDirFormat.initialize_ex_1.16',
 
247
                request_network_name, path, *args)
 
248
        except errors.UnknownSmartMethod:
 
249
            client._medium._remember_remote_is_before((1,16))
 
250
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
251
            self._supply_sub_formats_to(local_dir_format)
 
252
            return local_dir_format.initialize_on_transport_ex(transport,
 
253
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
254
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
255
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
256
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
257
                vfs_only=True)
 
258
        except errors.ErrorFromSmartServer, err:
 
259
            _translate_error(err, path=path)
 
260
        repo_path = response[0]
 
261
        bzrdir_name = response[6]
 
262
        require_stacking = response[7]
 
263
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
 
264
        format = RemoteBzrDirFormat()
 
265
        format._network_name = bzrdir_name
 
266
        self._supply_sub_formats_to(format)
 
267
        bzrdir = RemoteBzrDir(transport, format, _client=client)
 
268
        if repo_path:
 
269
            repo_format = response_tuple_to_repo_format(response[1:])
 
270
            if repo_path == '.':
 
271
                repo_path = ''
 
272
            if repo_path:
 
273
                repo_bzrdir_format = RemoteBzrDirFormat()
 
274
                repo_bzrdir_format._network_name = response[5]
 
275
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
 
276
                    repo_bzrdir_format)
 
277
            else:
 
278
                repo_bzr = bzrdir
 
279
            final_stack = response[8] or None
 
280
            final_stack_pwd = response[9] or None
 
281
            if final_stack_pwd:
 
282
                final_stack_pwd = urlutils.join(
 
283
                    transport.base, final_stack_pwd)
 
284
            remote_repo = RemoteRepository(repo_bzr, repo_format)
 
285
            if len(response) > 10:
 
286
                # Updated server verb that locks remotely.
 
287
                repo_lock_token = response[10] or None
 
288
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
 
289
                if repo_lock_token:
 
290
                    remote_repo.dont_leave_lock_in_place()
 
291
            else:
 
292
                remote_repo.lock_write()
 
293
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
 
294
                final_stack_pwd, require_stacking)
 
295
            policy.acquire_repository()
 
296
        else:
 
297
            remote_repo = None
 
298
            policy = None
 
299
        bzrdir._format.set_branch_format(self.get_branch_format())
 
300
        if require_stacking:
 
301
            # The repo has already been created, but we need to make sure that
 
302
            # we'll make a stackable branch.
 
303
            bzrdir._format.require_stacking(_skip_repo=True)
 
304
        return remote_repo, bzrdir, require_stacking, policy
 
305
 
 
306
    def _open(self, transport):
 
307
        return RemoteBzrDir(transport, self)
 
308
 
 
309
    def __eq__(self, other):
 
310
        if not isinstance(other, RemoteBzrDirFormat):
 
311
            return False
 
312
        return self.get_format_description() == other.get_format_description()
 
313
 
 
314
    def __return_repository_format(self):
 
315
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
 
316
        # repository format has been asked for, tell the RemoteRepositoryFormat
 
317
        # that it should use that for init() etc.
 
318
        result = RemoteRepositoryFormat()
 
319
        custom_format = getattr(self, '_repository_format', None)
 
320
        if custom_format:
 
321
            if isinstance(custom_format, RemoteRepositoryFormat):
 
322
                return custom_format
 
323
            else:
 
324
                # We will use the custom format to create repositories over the
 
325
                # wire; expose its details like rich_root_data for code to
 
326
                # query
 
327
                result._custom_format = custom_format
 
328
        return result
 
329
 
 
330
    def get_branch_format(self):
 
331
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
 
332
        if not isinstance(result, RemoteBranchFormat):
 
333
            new_result = RemoteBranchFormat()
 
334
            new_result._custom_format = result
 
335
            # cache the result
 
336
            self.set_branch_format(new_result)
 
337
            result = new_result
 
338
        return result
 
339
 
 
340
    repository_format = property(__return_repository_format,
 
341
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
 
342
 
 
343
 
 
344
class RemoteControlStore(config.IniFileStore):
 
345
    """Control store which attempts to use HPSS calls to retrieve control store.
 
346
 
 
347
    Note that this is specific to bzr-based formats.
 
348
    """
 
349
 
 
350
    def __init__(self, bzrdir):
 
351
        super(RemoteControlStore, self).__init__()
 
352
        self.bzrdir = bzrdir
 
353
        self._real_store = None
 
354
 
 
355
    def lock_write(self, token=None):
 
356
        self._ensure_real()
 
357
        return self._real_store.lock_write(token)
 
358
 
 
359
    def unlock(self):
 
360
        self._ensure_real()
 
361
        return self._real_store.unlock()
 
362
 
 
363
    @needs_write_lock
 
364
    def save(self):
 
365
        # We need to be able to override the undecorated implementation
 
366
        self.save_without_locking()
 
367
 
 
368
    def save_without_locking(self):
 
369
        super(RemoteControlStore, self).save()
 
370
 
 
371
    def _ensure_real(self):
 
372
        self.bzrdir._ensure_real()
 
373
        if self._real_store is None:
 
374
            self._real_store = config.ControlStore(self.bzrdir)
 
375
 
 
376
    def external_url(self):
 
377
        return self.bzrdir.user_url
 
378
 
 
379
    def _load_content(self):
 
380
        medium = self.bzrdir._client._medium
 
381
        path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
 
382
        try:
 
383
            response, handler = self.bzrdir._call_expecting_body(
 
384
                'BzrDir.get_config_file', path)
 
385
        except errors.UnknownSmartMethod:
 
386
            self._ensure_real()
 
387
            return self._real_store._load_content()
 
388
        if len(response) and response[0] != 'ok':
 
389
            raise errors.UnexpectedSmartServerResponse(response)
 
390
        return handler.read_body_bytes()
 
391
 
 
392
    def _save_content(self, content):
 
393
        # FIXME JRV 2011-11-22: Ideally this should use a
 
394
        # HPSS call too, but at the moment it is not possible
 
395
        # to write lock control directories.
 
396
        self._ensure_real()
 
397
        return self._real_store._save_content(content)
 
398
 
 
399
 
 
400
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
401
    """Control directory on a remote server, accessed via bzr:// or similar."""
93
402
 
94
403
    def __init__(self, transport, format, _client=None, _force_probe=False):
97
406
        :param _client: Private parameter for testing. Disables probing and the
98
407
            use of a real bzrdir.
99
408
        """
100
 
        BzrDir.__init__(self, transport, format)
 
409
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
101
410
        # this object holds a delegated bzrdir that uses file-level operations
102
411
        # to talk to the other side
103
412
        self._real_bzrdir = None
163
472
                import traceback
164
473
                warning('VFS BzrDir access triggered\n%s',
165
474
                    ''.join(traceback.format_stack()))
166
 
            self._real_bzrdir = BzrDir.open_from_transport(
 
475
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
167
476
                self.root_transport, _server_formats=False)
168
477
            self._format._network_name = \
169
478
                self._real_bzrdir._format.network_name()
175
484
        # Prevent aliasing problems in the next_open_branch_result cache.
176
485
        # See create_branch for rationale.
177
486
        self._next_open_branch_result = None
178
 
        return BzrDir.break_lock(self)
 
487
        return _mod_bzrdir.BzrDir.break_lock(self)
179
488
 
180
489
    def _vfs_cloning_metadir(self, require_stacking=False):
181
490
        self._ensure_real()
212
521
        if len(branch_info) != 2:
213
522
            raise errors.UnexpectedSmartServerResponse(response)
214
523
        branch_ref, branch_name = branch_info
215
 
        format = bzrdir.network_format_registry.get(control_name)
 
524
        try:
 
525
            format = controldir.network_format_registry.get(control_name)
 
526
        except KeyError:
 
527
            raise errors.UnknownFormatError(kind='control', format=control_name)
 
528
 
216
529
        if repo_name:
217
 
            format.repository_format = repository.network_format_registry.get(
218
 
                repo_name)
 
530
            try:
 
531
                format.repository_format = _mod_repository.network_format_registry.get(
 
532
                    repo_name)
 
533
            except KeyError:
 
534
                raise errors.UnknownFormatError(kind='repository',
 
535
                    format=repo_name)
219
536
        if branch_ref == 'ref':
220
537
            # XXX: we need possible_transports here to avoid reopening the
221
538
            # connection to the referenced location
222
 
            ref_bzrdir = BzrDir.open(branch_name)
 
539
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
540
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
541
            format.set_branch_format(branch_format)
225
542
        elif branch_ref == 'branch':
226
543
            if branch_name:
227
 
                format.set_branch_format(
228
 
                    branch.network_format_registry.get(branch_name))
 
544
                try:
 
545
                    branch_format = branch.network_format_registry.get(
 
546
                        branch_name)
 
547
                except KeyError:
 
548
                    raise errors.UnknownFormatError(kind='branch',
 
549
                        format=branch_name)
 
550
                format.set_branch_format(branch_format)
229
551
        else:
230
552
            raise errors.UnexpectedSmartServerResponse(response)
231
553
        return format
241
563
 
242
564
    def destroy_repository(self):
243
565
        """See BzrDir.destroy_repository"""
244
 
        self._ensure_real()
245
 
        self._real_bzrdir.destroy_repository()
 
566
        path = self._path_for_remote_call(self._client)
 
567
        try:
 
568
            response = self._call('BzrDir.destroy_repository', path)
 
569
        except errors.UnknownSmartMethod:
 
570
            self._ensure_real()
 
571
            self._real_bzrdir.destroy_repository()
 
572
            return
 
573
        if response[0] != 'ok':
 
574
            raise SmartProtocolError('unexpected response code %s' % (response,))
246
575
 
247
 
    def create_branch(self, name=None):
 
576
    def create_branch(self, name=None, repository=None,
 
577
                      append_revisions_only=None):
248
578
        # as per meta1 formats - just delegate to the format object which may
249
579
        # be parameterised.
250
580
        real_branch = self._format.get_branch_format().initialize(self,
251
 
            name=name)
 
581
            name=name, repository=repository,
 
582
            append_revisions_only=append_revisions_only)
252
583
        if not isinstance(real_branch, RemoteBranch):
253
 
            result = RemoteBranch(self, self.find_repository(), real_branch,
254
 
                                  name=name)
 
584
            if not isinstance(repository, RemoteRepository):
 
585
                raise AssertionError(
 
586
                    'need a RemoteRepository to use with RemoteBranch, got %r'
 
587
                    % (repository,))
 
588
            result = RemoteBranch(self, repository, real_branch, name=name)
255
589
        else:
256
590
            result = real_branch
257
591
        # BzrDir.clone_on_transport() uses the result of create_branch but does
265
599
 
266
600
    def destroy_branch(self, name=None):
267
601
        """See BzrDir.destroy_branch"""
268
 
        self._ensure_real()
269
 
        self._real_bzrdir.destroy_branch(name=name)
 
602
        path = self._path_for_remote_call(self._client)
 
603
        try:
 
604
            if name is not None:
 
605
                args = (name, )
 
606
            else:
 
607
                args = ()
 
608
            response = self._call('BzrDir.destroy_branch', path, *args)
 
609
        except errors.UnknownSmartMethod:
 
610
            self._ensure_real()
 
611
            self._real_bzrdir.destroy_branch(name=name)
 
612
            self._next_open_branch_result = None
 
613
            return
270
614
        self._next_open_branch_result = None
 
615
        if response[0] != 'ok':
 
616
            raise SmartProtocolError('unexpected response code %s' % (response,))
271
617
 
272
 
    def create_workingtree(self, revision_id=None, from_branch=None):
 
618
    def create_workingtree(self, revision_id=None, from_branch=None,
 
619
        accelerator_tree=None, hardlink=False):
273
620
        raise errors.NotLocalUrl(self.transport.base)
274
621
 
275
 
    def find_branch_format(self):
 
622
    def find_branch_format(self, name=None):
276
623
        """Find the branch 'format' for this bzrdir.
277
624
 
278
625
        This might be a synthetic object for e.g. RemoteBranch and SVN.
279
626
        """
280
 
        b = self.open_branch()
 
627
        b = self.open_branch(name=name)
281
628
        return b._format
282
629
 
283
 
    def get_branch_reference(self):
 
630
    def get_branch_reference(self, name=None):
284
631
        """See BzrDir.get_branch_reference()."""
 
632
        if name is not None:
 
633
            # XXX JRV20100304: Support opening colocated branches
 
634
            raise errors.NoColocatedBranchSupport(self)
285
635
        response = self._get_branch_reference()
286
636
        if response[0] == 'ref':
287
637
            return response[1]
318
668
            raise errors.UnexpectedSmartServerResponse(response)
319
669
        return response
320
670
 
321
 
    def _get_tree_branch(self):
 
671
    def _get_tree_branch(self, name=None):
322
672
        """See BzrDir._get_tree_branch()."""
323
 
        return None, self.open_branch()
 
673
        return None, self.open_branch(name=name)
324
674
 
325
675
    def open_branch(self, name=None, unsupported=False,
326
 
                    ignore_fallbacks=False):
 
676
                    ignore_fallbacks=False, possible_transports=None):
327
677
        if unsupported:
328
678
            raise NotImplementedError('unsupported flag support not implemented yet.')
329
679
        if self._next_open_branch_result is not None:
336
686
            # a branch reference, use the existing BranchReference logic.
337
687
            format = BranchReferenceFormat()
338
688
            return format.open(self, name=name, _found=True,
339
 
                location=response[1], ignore_fallbacks=ignore_fallbacks)
 
689
                location=response[1], ignore_fallbacks=ignore_fallbacks,
 
690
                possible_transports=possible_transports)
340
691
        branch_format_name = response[1]
341
692
        if not branch_format_name:
342
693
            branch_format_name = None
343
694
        format = RemoteBranchFormat(network_name=branch_format_name)
344
695
        return RemoteBranch(self, self.find_repository(), format=format,
345
 
            setup_stacking=not ignore_fallbacks, name=name)
 
696
            setup_stacking=not ignore_fallbacks, name=name,
 
697
            possible_transports=possible_transports)
346
698
 
347
699
    def _open_repo_v1(self, path):
348
700
        verb = 'BzrDir.find_repository'
411
763
 
412
764
    def has_workingtree(self):
413
765
        if self._has_working_tree is None:
414
 
            self._ensure_real()
415
 
            self._has_working_tree = self._real_bzrdir.has_workingtree()
 
766
            path = self._path_for_remote_call(self._client)
 
767
            try:
 
768
                response = self._call('BzrDir.has_workingtree', path)
 
769
            except errors.UnknownSmartMethod:
 
770
                self._ensure_real()
 
771
                self._has_working_tree = self._real_bzrdir.has_workingtree()
 
772
            else:
 
773
                if response[0] not in ('yes', 'no'):
 
774
                    raise SmartProtocolError('unexpected response code %s' % (response,))
 
775
                self._has_working_tree = (response[0] == 'yes')
416
776
        return self._has_working_tree
417
777
 
418
778
    def open_workingtree(self, recommend_upgrade=True):
423
783
 
424
784
    def _path_for_remote_call(self, client):
425
785
        """Return the path to be used for this bzrdir in a remote call."""
426
 
        return client.remote_path_from_transport(self.root_transport)
 
786
        return urlutils.split_segment_parameters_raw(
 
787
            client.remote_path_from_transport(self.root_transport))[0]
427
788
 
428
789
    def get_branch_transport(self, branch_format, name=None):
429
790
        self._ensure_real()
441
802
        """Upgrading of remote bzrdirs is not supported yet."""
442
803
        return False
443
804
 
444
 
    def needs_format_conversion(self, format=None):
 
805
    def needs_format_conversion(self, format):
445
806
        """Upgrading of remote bzrdirs is not supported yet."""
446
 
        if format is None:
447
 
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
448
 
                % 'needs_format_conversion(format=None)')
449
807
        return False
450
808
 
451
 
    def clone(self, url, revision_id=None, force_new_repo=False,
452
 
              preserve_stacking=False):
453
 
        self._ensure_real()
454
 
        return self._real_bzrdir.clone(url, revision_id=revision_id,
455
 
            force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
456
 
 
457
809
    def _get_config(self):
458
810
        return RemoteBzrDirConfig(self)
459
811
 
460
 
 
461
 
class RemoteRepositoryFormat(repository.RepositoryFormat):
 
812
    def _get_config_store(self):
 
813
        return RemoteControlStore(self)
 
814
 
 
815
 
 
816
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
817
    """Format for repositories accessed over a _SmartClient.
463
818
 
464
819
    Instances of this repository are represented by RemoteRepository
479
834
    """
480
835
 
481
836
    _matchingbzrdir = RemoteBzrDirFormat()
 
837
    supports_full_versioned_files = True
 
838
    supports_leaving_lock = True
482
839
 
483
840
    def __init__(self):
484
 
        repository.RepositoryFormat.__init__(self)
 
841
        _mod_repository.RepositoryFormat.__init__(self)
485
842
        self._custom_format = None
486
843
        self._network_name = None
487
844
        self._creating_bzrdir = None
 
845
        self._revision_graph_can_have_wrong_parents = None
488
846
        self._supports_chks = None
489
847
        self._supports_external_lookups = None
490
848
        self._supports_tree_reference = None
 
849
        self._supports_funky_characters = None
 
850
        self._supports_nesting_repositories = None
491
851
        self._rich_root_data = None
492
852
 
493
853
    def __repr__(self):
522
882
        return self._supports_external_lookups
523
883
 
524
884
    @property
 
885
    def supports_funky_characters(self):
 
886
        if self._supports_funky_characters is None:
 
887
            self._ensure_real()
 
888
            self._supports_funky_characters = \
 
889
                self._custom_format.supports_funky_characters
 
890
        return self._supports_funky_characters
 
891
 
 
892
    @property
 
893
    def supports_nesting_repositories(self):
 
894
        if self._supports_nesting_repositories is None:
 
895
            self._ensure_real()
 
896
            self._supports_nesting_repositories = \
 
897
                self._custom_format.supports_nesting_repositories
 
898
        return self._supports_nesting_repositories
 
899
 
 
900
    @property
525
901
    def supports_tree_reference(self):
526
902
        if self._supports_tree_reference is None:
527
903
            self._ensure_real()
529
905
                self._custom_format.supports_tree_reference
530
906
        return self._supports_tree_reference
531
907
 
 
908
    @property
 
909
    def revision_graph_can_have_wrong_parents(self):
 
910
        if self._revision_graph_can_have_wrong_parents is None:
 
911
            self._ensure_real()
 
912
            self._revision_graph_can_have_wrong_parents = \
 
913
                self._custom_format.revision_graph_can_have_wrong_parents
 
914
        return self._revision_graph_can_have_wrong_parents
 
915
 
532
916
    def _vfs_initialize(self, a_bzrdir, shared):
533
917
        """Helper for common code in initialize."""
534
918
        if self._custom_format:
569
953
            network_name = self._network_name
570
954
        else:
571
955
            # Select the current bzrlib default and ask for that.
572
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
956
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
573
957
            reference_format = reference_bzrdir_format.repository_format
574
958
            network_name = reference_format.network_name()
575
959
        # 2) try direct creation via RPC
601
985
 
602
986
    def _ensure_real(self):
603
987
        if self._custom_format is None:
604
 
            self._custom_format = repository.network_format_registry.get(
605
 
                self._network_name)
 
988
            try:
 
989
                self._custom_format = _mod_repository.network_format_registry.get(
 
990
                    self._network_name)
 
991
            except KeyError:
 
992
                raise errors.UnknownFormatError(kind='repository',
 
993
                    format=self._network_name)
606
994
 
607
995
    @property
608
996
    def _fetch_order(self):
643
1031
        return self._custom_format._serializer
644
1032
 
645
1033
 
646
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
647
 
    bzrdir.ControlComponent):
 
1034
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
 
1035
        lock._RelockDebugMixin):
648
1036
    """Repository accessed over rpc.
649
1037
 
650
1038
    For the moment most operations are performed using local transport-backed
674
1062
        self._format = format
675
1063
        self._lock_mode = None
676
1064
        self._lock_token = None
 
1065
        self._write_group_tokens = None
677
1066
        self._lock_count = 0
678
1067
        self._leave_lock = False
679
1068
        # Cache of revision parents; misses are cached during read locks, and
703
1092
        # transport, but I'm not sure it's worth making this method
704
1093
        # optional -- mbp 2010-04-21
705
1094
        return self.bzrdir.get_repository_transport(None)
706
 
        
 
1095
 
707
1096
    def __str__(self):
708
1097
        return "%s(%s)" % (self.__class__.__name__, self.base)
709
1098
 
719
1108
 
720
1109
        :param suppress_errors: see Repository.abort_write_group.
721
1110
        """
722
 
        self._ensure_real()
723
 
        return self._real_repository.abort_write_group(
724
 
            suppress_errors=suppress_errors)
 
1111
        if self._real_repository:
 
1112
            self._ensure_real()
 
1113
            return self._real_repository.abort_write_group(
 
1114
                suppress_errors=suppress_errors)
 
1115
        if not self.is_in_write_group():
 
1116
            if suppress_errors:
 
1117
                mutter('(suppressed) not in write group')
 
1118
                return
 
1119
            raise errors.BzrError("not in write group")
 
1120
        path = self.bzrdir._path_for_remote_call(self._client)
 
1121
        try:
 
1122
            response = self._call('Repository.abort_write_group', path,
 
1123
                self._lock_token, self._write_group_tokens)
 
1124
        except Exception, exc:
 
1125
            self._write_group = None
 
1126
            if not suppress_errors:
 
1127
                raise
 
1128
            mutter('abort_write_group failed')
 
1129
            log_exception_quietly()
 
1130
            note(gettext('bzr: ERROR (ignored): %s'), exc)
 
1131
        else:
 
1132
            if response != ('ok', ):
 
1133
                raise errors.UnexpectedSmartServerResponse(response)
 
1134
            self._write_group_tokens = None
725
1135
 
726
1136
    @property
727
1137
    def chk_bytes(self):
741
1151
        for older plugins that don't use e.g. the CommitBuilder
742
1152
        facility.
743
1153
        """
744
 
        self._ensure_real()
745
 
        return self._real_repository.commit_write_group()
 
1154
        if self._real_repository:
 
1155
            self._ensure_real()
 
1156
            return self._real_repository.commit_write_group()
 
1157
        if not self.is_in_write_group():
 
1158
            raise errors.BzrError("not in write group")
 
1159
        path = self.bzrdir._path_for_remote_call(self._client)
 
1160
        response = self._call('Repository.commit_write_group', path,
 
1161
            self._lock_token, self._write_group_tokens)
 
1162
        if response != ('ok', ):
 
1163
            raise errors.UnexpectedSmartServerResponse(response)
 
1164
        self._write_group_tokens = None
746
1165
 
747
1166
    def resume_write_group(self, tokens):
748
 
        self._ensure_real()
749
 
        return self._real_repository.resume_write_group(tokens)
 
1167
        if self._real_repository:
 
1168
            return self._real_repository.resume_write_group(tokens)
 
1169
        path = self.bzrdir._path_for_remote_call(self._client)
 
1170
        try:
 
1171
            response = self._call('Repository.check_write_group', path,
 
1172
               self._lock_token, tokens)
 
1173
        except errors.UnknownSmartMethod:
 
1174
            self._ensure_real()
 
1175
            return self._real_repository.resume_write_group(tokens)
 
1176
        if response != ('ok', ):
 
1177
            raise errors.UnexpectedSmartServerResponse(response)
 
1178
        self._write_group_tokens = tokens
750
1179
 
751
1180
    def suspend_write_group(self):
752
 
        self._ensure_real()
753
 
        return self._real_repository.suspend_write_group()
 
1181
        if self._real_repository:
 
1182
            return self._real_repository.suspend_write_group()
 
1183
        ret = self._write_group_tokens or []
 
1184
        self._write_group_tokens = None
 
1185
        return ret
754
1186
 
755
1187
    def get_missing_parent_inventories(self, check_for_missing_texts=True):
756
1188
        self._ensure_real()
817
1249
    def find_text_key_references(self):
818
1250
        """Find the text key references within the repository.
819
1251
 
820
 
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
821
 
        revision_ids. Each altered file-ids has the exact revision_ids that
822
 
        altered it listed explicitly.
823
1252
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
824
1253
            to whether they were referred to by the inventory of the
825
1254
            revision_id that they contain. The inventory texts from all present
843
1272
        """Private method for using with old (< 1.2) servers to fallback."""
844
1273
        if revision_id is None:
845
1274
            revision_id = ''
846
 
        elif revision.is_null(revision_id):
 
1275
        elif _mod_revision.is_null(revision_id):
847
1276
            return {}
848
1277
 
849
1278
        path = self.bzrdir._path_for_remote_call(self._client)
873
1302
        return RemoteStreamSource(self, to_format)
874
1303
 
875
1304
    @needs_read_lock
 
1305
    def get_file_graph(self):
 
1306
        return graph.Graph(self.texts)
 
1307
 
 
1308
    @needs_read_lock
876
1309
    def has_revision(self, revision_id):
877
1310
        """True if this repository has a copy of the revision."""
878
1311
        # Copy of bzrlib.repository.Repository.has_revision
895
1328
    def _has_same_fallbacks(self, other_repo):
896
1329
        """Returns true if the repositories have the same fallbacks."""
897
1330
        # XXX: copied from Repository; it should be unified into a base class
898
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
 
1331
        # <https://bugs.launchpad.net/bzr/+bug/401622>
899
1332
        my_fb = self._fallback_repositories
900
1333
        other_fb = other_repo._fallback_repositories
901
1334
        if len(my_fb) != len(other_fb):
930
1363
        """See Repository.gather_stats()."""
931
1364
        path = self.bzrdir._path_for_remote_call(self._client)
932
1365
        # revid can be None to indicate no revisions, not just NULL_REVISION
933
 
        if revid is None or revision.is_null(revid):
 
1366
        if revid is None or _mod_revision.is_null(revid):
934
1367
            fmt_revid = ''
935
1368
        else:
936
1369
            fmt_revid = revid
965
1398
 
966
1399
    def get_physical_lock_status(self):
967
1400
        """See Repository.get_physical_lock_status()."""
968
 
        # should be an API call to the server.
969
 
        self._ensure_real()
970
 
        return self._real_repository.get_physical_lock_status()
 
1401
        path = self.bzrdir._path_for_remote_call(self._client)
 
1402
        try:
 
1403
            response = self._call('Repository.get_physical_lock_status', path)
 
1404
        except errors.UnknownSmartMethod:
 
1405
            self._ensure_real()
 
1406
            return self._real_repository.get_physical_lock_status()
 
1407
        if response[0] not in ('yes', 'no'):
 
1408
            raise errors.UnexpectedSmartServerResponse(response)
 
1409
        return (response[0] == 'yes')
971
1410
 
972
1411
    def is_in_write_group(self):
973
1412
        """Return True if there is an open write group.
974
1413
 
975
1414
        write groups are only applicable locally for the smart server..
976
1415
        """
 
1416
        if self._write_group_tokens is not None:
 
1417
            return True
977
1418
        if self._real_repository:
978
1419
            return self._real_repository.is_in_write_group()
979
1420
 
997
1438
        pass
998
1439
 
999
1440
    def lock_read(self):
 
1441
        """Lock the repository for read operations.
 
1442
 
 
1443
        :return: A bzrlib.lock.LogicalLockResult.
 
1444
        """
1000
1445
        # wrong eventually - want a local lock cache context
1001
1446
        if not self._lock_mode:
1002
1447
            self._note_lock('r')
1009
1454
                repo.lock_read()
1010
1455
        else:
1011
1456
            self._lock_count += 1
 
1457
        return lock.LogicalLockResult(self.unlock)
1012
1458
 
1013
1459
    def _remote_lock_write(self, token):
1014
1460
        path = self.bzrdir._path_for_remote_call(self._client)
1054
1500
            raise errors.ReadOnlyError(self)
1055
1501
        else:
1056
1502
            self._lock_count += 1
1057
 
        return self._lock_token or None
 
1503
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
1058
1504
 
1059
1505
    def leave_lock_in_place(self):
1060
1506
        if not self._lock_token:
1109
1555
            self._real_repository.lock_write(self._lock_token)
1110
1556
        elif self._lock_mode == 'r':
1111
1557
            self._real_repository.lock_read()
 
1558
        if self._write_group_tokens is not None:
 
1559
            # if we are already in a write group, resume it
 
1560
            self._real_repository.resume_write_group(self._write_group_tokens)
 
1561
            self._write_group_tokens = None
1112
1562
 
1113
1563
    def start_write_group(self):
1114
1564
        """Start a write group on the decorated repository.
1118
1568
        for older plugins that don't use e.g. the CommitBuilder
1119
1569
        facility.
1120
1570
        """
1121
 
        self._ensure_real()
1122
 
        return self._real_repository.start_write_group()
 
1571
        if self._real_repository:
 
1572
            self._ensure_real()
 
1573
            return self._real_repository.start_write_group()
 
1574
        if not self.is_write_locked():
 
1575
            raise errors.NotWriteLocked(self)
 
1576
        if self._write_group_tokens is not None:
 
1577
            raise errors.BzrError('already in a write group')
 
1578
        path = self.bzrdir._path_for_remote_call(self._client)
 
1579
        try:
 
1580
            response = self._call('Repository.start_write_group', path,
 
1581
                self._lock_token)
 
1582
        except (errors.UnknownSmartMethod, errors.UnsuspendableWriteGroup):
 
1583
            self._ensure_real()
 
1584
            return self._real_repository.start_write_group()
 
1585
        if response[0] != 'ok':
 
1586
            raise errors.UnexpectedSmartServerResponse(response)
 
1587
        self._write_group_tokens = response[1]
1123
1588
 
1124
1589
    def _unlock(self, token):
1125
1590
        path = self.bzrdir._path_for_remote_call(self._client)
1152
1617
            # This is just to let the _real_repository stay up to date.
1153
1618
            if self._real_repository is not None:
1154
1619
                self._real_repository.unlock()
 
1620
            elif self._write_group_tokens is not None:
 
1621
                self.abort_write_group()
1155
1622
        finally:
1156
1623
            # The rpc-level lock should be released even if there was a
1157
1624
            # problem releasing the vfs-based lock.
1169
1636
 
1170
1637
    def break_lock(self):
1171
1638
        # should hand off to the network
1172
 
        self._ensure_real()
1173
 
        return self._real_repository.break_lock()
 
1639
        path = self.bzrdir._path_for_remote_call(self._client)
 
1640
        try:
 
1641
            response = self._call("Repository.break_lock", path)
 
1642
        except errors.UnknownSmartMethod:
 
1643
            self._ensure_real()
 
1644
            return self._real_repository.break_lock()
 
1645
        if response != ('ok',):
 
1646
            raise errors.UnexpectedSmartServerResponse(response)
1174
1647
 
1175
1648
    def _get_tarball(self, compression):
1176
1649
        """Return a TemporaryFile containing a repository tarball.
1194
1667
            return t
1195
1668
        raise errors.UnexpectedSmartServerResponse(response)
1196
1669
 
 
1670
    @needs_read_lock
1197
1671
    def sprout(self, to_bzrdir, revision_id=None):
1198
 
        # TODO: Option to control what format is created?
1199
 
        self._ensure_real()
1200
 
        dest_repo = self._real_repository._format.initialize(to_bzrdir,
1201
 
                                                             shared=False)
 
1672
        """Create a descendent repository for new development.
 
1673
 
 
1674
        Unlike clone, this does not copy the settings of the repository.
 
1675
        """
 
1676
        dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1202
1677
        dest_repo.fetch(self, revision_id=revision_id)
1203
1678
        return dest_repo
1204
1679
 
 
1680
    def _create_sprouting_repo(self, a_bzrdir, shared):
 
1681
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
 
1682
            # use target default format.
 
1683
            dest_repo = a_bzrdir.create_repository()
 
1684
        else:
 
1685
            # Most control formats need the repository to be specifically
 
1686
            # created, but on some old all-in-one formats it's not needed
 
1687
            try:
 
1688
                dest_repo = self._format.initialize(a_bzrdir, shared=shared)
 
1689
            except errors.UninitializableFormat:
 
1690
                dest_repo = a_bzrdir.open_repository()
 
1691
        return dest_repo
 
1692
 
1205
1693
    ### These methods are just thin shims to the VFS object for now.
1206
1694
 
 
1695
    @needs_read_lock
1207
1696
    def revision_tree(self, revision_id):
1208
 
        self._ensure_real()
1209
 
        return self._real_repository.revision_tree(revision_id)
 
1697
        revision_id = _mod_revision.ensure_null(revision_id)
 
1698
        if revision_id == _mod_revision.NULL_REVISION:
 
1699
            return InventoryRevisionTree(self,
 
1700
                Inventory(root_id=None), _mod_revision.NULL_REVISION)
 
1701
        else:
 
1702
            return list(self.revision_trees([revision_id]))[0]
1210
1703
 
1211
1704
    def get_serializer_format(self):
1212
 
        self._ensure_real()
1213
 
        return self._real_repository.get_serializer_format()
 
1705
        path = self.bzrdir._path_for_remote_call(self._client)
 
1706
        try:
 
1707
            response = self._call('VersionedFileRepository.get_serializer_format',
 
1708
                path)
 
1709
        except errors.UnknownSmartMethod:
 
1710
            self._ensure_real()
 
1711
            return self._real_repository.get_serializer_format()
 
1712
        if response[0] != 'ok':
 
1713
            raise errors.UnexpectedSmartServerResponse(response)
 
1714
        return response[1]
1214
1715
 
1215
1716
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1717
                           timezone=None, committer=None, revprops=None,
1217
 
                           revision_id=None):
 
1718
                           revision_id=None, lossy=False):
1218
1719
        # FIXME: It ought to be possible to call this without immediately
1219
1720
        # triggering _ensure_real.  For now it's the easiest thing to do.
1220
1721
        self._ensure_real()
1221
1722
        real_repo = self._real_repository
1222
1723
        builder = real_repo.get_commit_builder(branch, parents,
1223
1724
                config, timestamp=timestamp, timezone=timezone,
1224
 
                committer=committer, revprops=revprops, revision_id=revision_id)
 
1725
                committer=committer, revprops=revprops,
 
1726
                revision_id=revision_id, lossy=lossy)
1225
1727
        return builder
1226
1728
 
1227
1729
    def add_fallback_repository(self, repository):
1235
1737
        # We need to accumulate additional repositories here, to pass them in
1236
1738
        # on various RPC's.
1237
1739
        #
 
1740
        # Make the check before we lock: this raises an exception.
 
1741
        self._check_fallback_repository(repository)
1238
1742
        if self.is_locked():
1239
1743
            # We will call fallback.unlock() when we transition to the unlocked
1240
1744
            # state, so always add a lock here. If a caller passes us a locked
1241
1745
            # repository, they are responsible for unlocking it later.
1242
1746
            repository.lock_read()
1243
 
        self._check_fallback_repository(repository)
1244
1747
        self._fallback_repositories.append(repository)
1245
1748
        # If self._real_repository was parameterised already (e.g. because a
1246
1749
        # _real_branch had its get_stacked_on_url method called), then the
1288
1791
 
1289
1792
    @needs_read_lock
1290
1793
    def get_revision(self, revision_id):
1291
 
        self._ensure_real()
1292
 
        return self._real_repository.get_revision(revision_id)
 
1794
        return self.get_revisions([revision_id])[0]
1293
1795
 
1294
1796
    def get_transaction(self):
1295
1797
        self._ensure_real()
1297
1799
 
1298
1800
    @needs_read_lock
1299
1801
    def clone(self, a_bzrdir, revision_id=None):
1300
 
        self._ensure_real()
1301
 
        return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
 
1802
        dest_repo = self._create_sprouting_repo(
 
1803
            a_bzrdir, shared=self.is_shared())
 
1804
        self.copy_content_into(dest_repo, revision_id)
 
1805
        return dest_repo
1302
1806
 
1303
1807
    def make_working_trees(self):
1304
1808
        """See Repository.make_working_trees"""
1305
 
        self._ensure_real()
1306
 
        return self._real_repository.make_working_trees()
 
1809
        path = self.bzrdir._path_for_remote_call(self._client)
 
1810
        try:
 
1811
            response = self._call('Repository.make_working_trees', path)
 
1812
        except errors.UnknownSmartMethod:
 
1813
            self._ensure_real()
 
1814
            return self._real_repository.make_working_trees()
 
1815
        if response[0] not in ('yes', 'no'):
 
1816
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
1817
        return response[0] == 'yes'
1307
1818
 
1308
1819
    def refresh_data(self):
1309
 
        """Re-read any data needed to to synchronise with disk.
 
1820
        """Re-read any data needed to synchronise with disk.
1310
1821
 
1311
1822
        This method is intended to be called after another repository instance
1312
1823
        (such as one used by a smart server) has inserted data into the
1313
 
        repository. It may not be called during a write group, but may be
1314
 
        called at any other time.
 
1824
        repository. On all repositories this will work outside of write groups.
 
1825
        Some repository formats (pack and newer for bzrlib native formats)
 
1826
        support refresh_data inside write groups. If called inside a write
 
1827
        group on a repository that does not support refreshing in a write group
 
1828
        IsInWriteGroupError will be raised.
1315
1829
        """
1316
 
        if self.is_in_write_group():
1317
 
            raise errors.InternalBzrError(
1318
 
                "May not refresh_data while in a write group.")
1319
1830
        if self._real_repository is not None:
1320
1831
            self._real_repository.refresh_data()
1321
1832
 
1333
1844
        return result
1334
1845
 
1335
1846
    @needs_read_lock
1336
 
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
1847
    def search_missing_revision_ids(self, other,
 
1848
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
1849
            find_ghosts=True, revision_ids=None, if_present_ids=None,
 
1850
            limit=None):
1337
1851
        """Return the revision ids that other has that this does not.
1338
1852
 
1339
1853
        These are returned in topological order.
1340
1854
 
1341
1855
        revision_id: only return revision ids included by revision_id.
1342
1856
        """
1343
 
        return repository.InterRepository.get(
1344
 
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
 
1857
        if symbol_versioning.deprecated_passed(revision_id):
 
1858
            symbol_versioning.warn(
 
1859
                'search_missing_revision_ids(revision_id=...) was '
 
1860
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
1861
                DeprecationWarning, stacklevel=2)
 
1862
            if revision_ids is not None:
 
1863
                raise AssertionError(
 
1864
                    'revision_ids is mutually exclusive with revision_id')
 
1865
            if revision_id is not None:
 
1866
                revision_ids = [revision_id]
 
1867
        inter_repo = _mod_repository.InterRepository.get(other, self)
 
1868
        return inter_repo.search_missing_revision_ids(
 
1869
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
1870
            if_present_ids=if_present_ids, limit=limit)
1345
1871
 
1346
 
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
 
1872
    def fetch(self, source, revision_id=None, find_ghosts=False,
1347
1873
            fetch_spec=None):
1348
1874
        # No base implementation to use as RemoteRepository is not a subclass
1349
1875
        # of Repository; so this is a copy of Repository.fetch().
1360
1886
            # check that last_revision is in 'from' and then return a
1361
1887
            # no-operation.
1362
1888
            if (revision_id is not None and
1363
 
                not revision.is_null(revision_id)):
 
1889
                not _mod_revision.is_null(revision_id)):
1364
1890
                self.get_revision(revision_id)
1365
1891
            return 0, []
1366
1892
        # if there is no specific appropriate InterRepository, this will get
1367
1893
        # the InterRepository base class, which raises an
1368
1894
        # IncompatibleRepositories when asked to fetch.
1369
 
        inter = repository.InterRepository.get(source, self)
1370
 
        return inter.fetch(revision_id=revision_id, pb=pb,
 
1895
        inter = _mod_repository.InterRepository.get(source, self)
 
1896
        return inter.fetch(revision_id=revision_id,
1371
1897
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1372
1898
 
1373
1899
    def create_bundle(self, target, base, fileobj, format=None):
1375
1901
        self._real_repository.create_bundle(target, base, fileobj, format)
1376
1902
 
1377
1903
    @needs_read_lock
 
1904
    @symbol_versioning.deprecated_method(
 
1905
        symbol_versioning.deprecated_in((2, 4, 0)))
1378
1906
    def get_ancestry(self, revision_id, topo_sorted=True):
1379
1907
        self._ensure_real()
1380
1908
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
1394
1922
        self._ensure_real()
1395
1923
        return self._real_repository.iter_files_bytes(desired_files)
1396
1924
 
 
1925
    def get_cached_parent_map(self, revision_ids):
 
1926
        """See bzrlib.CachingParentsProvider.get_cached_parent_map"""
 
1927
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
 
1928
 
1397
1929
    def get_parent_map(self, revision_ids):
1398
1930
        """See bzrlib.Graph.get_parent_map()."""
1399
1931
        return self._make_parents_provider().get_parent_map(revision_ids)
1457
1989
        if parents_map is None:
1458
1990
            # Repository is not locked, so there's no cache.
1459
1991
            parents_map = {}
1460
 
        # start_set is all the keys in the cache
1461
 
        start_set = set(parents_map)
1462
 
        # result set is all the references to keys in the cache
1463
 
        result_parents = set()
1464
 
        for parents in parents_map.itervalues():
1465
 
            result_parents.update(parents)
1466
 
        stop_keys = result_parents.difference(start_set)
1467
 
        # We don't need to send ghosts back to the server as a position to
1468
 
        # stop either.
1469
 
        stop_keys.difference_update(self._unstacked_provider.missing_keys)
1470
 
        key_count = len(parents_map)
1471
 
        if (NULL_REVISION in result_parents
1472
 
            and NULL_REVISION in self._unstacked_provider.missing_keys):
1473
 
            # If we pruned NULL_REVISION from the stop_keys because it's also
1474
 
            # in our cache of "missing" keys we need to increment our key count
1475
 
            # by 1, because the reconsitituted SearchResult on the server will
1476
 
            # still consider NULL_REVISION to be an included key.
1477
 
            key_count += 1
1478
 
        included_keys = start_set.intersection(result_parents)
1479
 
        start_set.difference_update(included_keys)
 
1992
        if _DEFAULT_SEARCH_DEPTH <= 0:
 
1993
            (start_set, stop_keys,
 
1994
             key_count) = graph.search_result_from_parent_map(
 
1995
                parents_map, self._unstacked_provider.missing_keys)
 
1996
        else:
 
1997
            (start_set, stop_keys,
 
1998
             key_count) = graph.limited_search_result_from_parent_map(
 
1999
                parents_map, self._unstacked_provider.missing_keys,
 
2000
                keys, depth=_DEFAULT_SEARCH_DEPTH)
1480
2001
        recipe = ('manual', start_set, stop_keys, key_count)
1481
2002
        body = self._serialise_search_recipe(recipe)
1482
2003
        path = self.bzrdir._path_for_remote_call(self._client)
1544
2065
        return self._real_repository.reconcile(other=other, thorough=thorough)
1545
2066
 
1546
2067
    def all_revision_ids(self):
1547
 
        self._ensure_real()
1548
 
        return self._real_repository.all_revision_ids()
 
2068
        path = self.bzrdir._path_for_remote_call(self._client)
 
2069
        try:
 
2070
            response_tuple, response_handler = self._call_expecting_body(
 
2071
                "Repository.all_revision_ids", path)
 
2072
        except errors.UnknownSmartMethod:
 
2073
            self._ensure_real()
 
2074
            return self._real_repository.all_revision_ids()
 
2075
        if response_tuple != ("ok", ):
 
2076
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2077
        revids = set(response_handler.read_body_bytes().splitlines())
 
2078
        for fallback in self._fallback_repositories:
 
2079
            revids.update(set(fallback.all_revision_ids()))
 
2080
        return list(revids)
1549
2081
 
1550
2082
    @needs_read_lock
1551
2083
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1555
2087
 
1556
2088
    @needs_read_lock
1557
2089
    def get_revision_delta(self, revision_id, specific_fileids=None):
1558
 
        self._ensure_real()
1559
 
        return self._real_repository.get_revision_delta(revision_id,
1560
 
            specific_fileids=specific_fileids)
 
2090
        r = self.get_revision(revision_id)
 
2091
        return list(self.get_deltas_for_revisions([r],
 
2092
            specific_fileids=specific_fileids))[0]
1561
2093
 
1562
2094
    @needs_read_lock
1563
2095
    def revision_trees(self, revision_ids):
1576
2108
            callback_refs=callback_refs, check_repo=check_repo)
1577
2109
 
1578
2110
    def copy_content_into(self, destination, revision_id=None):
1579
 
        self._ensure_real()
1580
 
        return self._real_repository.copy_content_into(
1581
 
            destination, revision_id=revision_id)
 
2111
        """Make a complete copy of the content in self into destination.
 
2112
 
 
2113
        This is a destructive operation! Do not use it on existing
 
2114
        repositories.
 
2115
        """
 
2116
        interrepo = _mod_repository.InterRepository.get(self, destination)
 
2117
        return interrepo.copy_content(revision_id)
1582
2118
 
1583
2119
    def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2120
        # get a tarball of the remote repository, and copy from that into the
1586
2122
        from bzrlib import osutils
1587
2123
        import tarfile
1588
2124
        # TODO: Maybe a progress bar while streaming the tarball?
1589
 
        note("Copying repository content as tarball...")
 
2125
        note(gettext("Copying repository content as tarball..."))
1590
2126
        tar_file = self._get_tarball('bz2')
1591
2127
        if tar_file is None:
1592
2128
            return None
1597
2133
            tmpdir = osutils.mkdtemp()
1598
2134
            try:
1599
2135
                _extract_tar(tar, tmpdir)
1600
 
                tmp_bzrdir = BzrDir.open(tmpdir)
 
2136
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1601
2137
                tmp_repo = tmp_bzrdir.open_repository()
1602
2138
                tmp_repo.copy_content_into(destination, revision_id)
1603
2139
            finally:
1621
2157
    @needs_write_lock
1622
2158
    def pack(self, hint=None, clean_obsolete_packs=False):
1623
2159
        """Compress the data within the repository.
1624
 
 
1625
 
        This is not currently implemented within the smart server.
1626
2160
        """
1627
 
        self._ensure_real()
1628
 
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
 
2161
        if hint is None:
 
2162
            body = ""
 
2163
        else:
 
2164
            body = "".join([l+"\n" for l in hint])
 
2165
        path = self.bzrdir._path_for_remote_call(self._client)
 
2166
        try:
 
2167
            response, handler = self._call_with_body_bytes_expecting_body(
 
2168
                'Repository.pack', (path, self._lock_token,
 
2169
                    str(clean_obsolete_packs)), body)
 
2170
        except errors.UnknownSmartMethod:
 
2171
            self._ensure_real()
 
2172
            return self._real_repository.pack(hint=hint,
 
2173
                clean_obsolete_packs=clean_obsolete_packs)
 
2174
        handler.cancel_read_body()
 
2175
        if response != ('ok', ):
 
2176
            raise errors.UnexpectedSmartServerResponse(response)
1629
2177
 
1630
2178
    @property
1631
2179
    def revisions(self):
1667
2215
 
1668
2216
    @needs_write_lock
1669
2217
    def sign_revision(self, revision_id, gpg_strategy):
1670
 
        self._ensure_real()
1671
 
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
 
2218
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2219
        plaintext = testament.as_short_text()
 
2220
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1672
2221
 
1673
2222
    @property
1674
2223
    def texts(self):
1688
2237
    def supports_rich_root(self):
1689
2238
        return self._format.rich_root_data
1690
2239
 
 
2240
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1691
2241
    def iter_reverse_revision_history(self, revision_id):
1692
2242
        self._ensure_real()
1693
2243
        return self._real_repository.iter_reverse_revision_history(revision_id)
1696
2246
    def _serializer(self):
1697
2247
        return self._format._serializer
1698
2248
 
 
2249
    @needs_write_lock
1699
2250
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1700
 
        self._ensure_real()
1701
 
        return self._real_repository.store_revision_signature(
1702
 
            gpg_strategy, plaintext, revision_id)
 
2251
        signature = gpg_strategy.sign(plaintext)
 
2252
        self.add_signature_text(revision_id, signature)
1703
2253
 
1704
2254
    def add_signature_text(self, revision_id, signature):
1705
 
        self._ensure_real()
1706
 
        return self._real_repository.add_signature_text(revision_id, signature)
 
2255
        if self._real_repository:
 
2256
            # If there is a real repository the write group will
 
2257
            # be in the real repository as well, so use that:
 
2258
            self._ensure_real()
 
2259
            return self._real_repository.add_signature_text(
 
2260
                revision_id, signature)
 
2261
        path = self.bzrdir._path_for_remote_call(self._client)
 
2262
        response, response_handler = self._call_with_body_bytes(
 
2263
            'Repository.add_signature_text', (path, revision_id),
 
2264
            signature)
 
2265
        self.refresh_data()
 
2266
        if response[0] != 'ok':
 
2267
            raise errors.UnexpectedSmartServerResponse(response)
1707
2268
 
1708
2269
    def has_signature_for_revision_id(self, revision_id):
1709
 
        self._ensure_real()
1710
 
        return self._real_repository.has_signature_for_revision_id(revision_id)
 
2270
        path = self.bzrdir._path_for_remote_call(self._client)
 
2271
        try:
 
2272
            response = self._call('Repository.has_signature_for_revision_id',
 
2273
                path, revision_id)
 
2274
        except errors.UnknownSmartMethod:
 
2275
            self._ensure_real()
 
2276
            return self._real_repository.has_signature_for_revision_id(
 
2277
                revision_id)
 
2278
        if response[0] not in ('yes', 'no'):
 
2279
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
2280
        return (response[0] == 'yes')
 
2281
 
 
2282
    @needs_read_lock
 
2283
    def verify_revision_signature(self, revision_id, gpg_strategy):
 
2284
        if not self.has_signature_for_revision_id(revision_id):
 
2285
            return gpg.SIGNATURE_NOT_SIGNED, None
 
2286
        signature = self.get_signature_text(revision_id)
 
2287
 
 
2288
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2289
        plaintext = testament.as_short_text()
 
2290
 
 
2291
        return gpg_strategy.verify(signature, plaintext)
1711
2292
 
1712
2293
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2294
        self._ensure_real()
1714
2295
        return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2296
            _files_pb=_files_pb)
1716
2297
 
1717
 
    def revision_graph_can_have_wrong_parents(self):
1718
 
        # The answer depends on the remote repo format.
1719
 
        self._ensure_real()
1720
 
        return self._real_repository.revision_graph_can_have_wrong_parents()
1721
 
 
1722
2298
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2299
        self._ensure_real()
1724
2300
        return self._real_repository._find_inconsistent_revision_parents(
1732
2308
        providers = [self._unstacked_provider]
1733
2309
        if other is not None:
1734
2310
            providers.insert(0, other)
1735
 
        providers.extend(r._make_parents_provider() for r in
1736
 
                         self._fallback_repositories)
1737
 
        return graph.StackedParentsProvider(providers)
 
2311
        return graph.StackedParentsProvider(_LazyListJoin(
 
2312
            providers, self._fallback_repositories))
1738
2313
 
1739
2314
    def _serialise_search_recipe(self, recipe):
1740
2315
        """Serialise a graph search recipe.
1748
2323
        return '\n'.join((start_keys, stop_keys, count))
1749
2324
 
1750
2325
    def _serialise_search_result(self, search_result):
1751
 
        if isinstance(search_result, graph.PendingAncestryResult):
1752
 
            parts = ['ancestry-of']
1753
 
            parts.extend(search_result.heads)
1754
 
        else:
1755
 
            recipe = search_result.get_recipe()
1756
 
            parts = [recipe[0], self._serialise_search_recipe(recipe)]
 
2326
        parts = search_result.get_network_struct()
1757
2327
        return '\n'.join(parts)
1758
2328
 
1759
2329
    def autopack(self):
1769
2339
            raise errors.UnexpectedSmartServerResponse(response)
1770
2340
 
1771
2341
 
1772
 
class RemoteStreamSink(repository.StreamSink):
 
2342
class RemoteStreamSink(vf_repository.StreamSink):
1773
2343
 
1774
2344
    def _insert_real(self, stream, src_format, resume_tokens):
1775
2345
        self.target_repo._ensure_real()
1876
2446
        self._last_substream and self._last_stream so that the stream can be
1877
2447
        resumed by _resume_stream_with_vfs.
1878
2448
        """
1879
 
                    
 
2449
 
1880
2450
        stream_iter = iter(stream)
1881
2451
        for substream_kind, substream in stream_iter:
1882
2452
            if substream_kind == 'inventory-deltas':
1885
2455
                return
1886
2456
            else:
1887
2457
                yield substream_kind, substream
1888
 
            
1889
 
 
1890
 
class RemoteStreamSource(repository.StreamSource):
 
2458
 
 
2459
 
 
2460
class RemoteStreamSource(vf_repository.StreamSource):
1891
2461
    """Stream data from a remote server."""
1892
2462
 
1893
2463
    def get_stream(self, search):
1953
2523
        candidate_verbs = [
1954
2524
            ('Repository.get_stream_1.19', (1, 19)),
1955
2525
            ('Repository.get_stream', (1, 13))]
 
2526
 
1956
2527
        found_verb = False
1957
2528
        for verb, version in candidate_verbs:
1958
2529
            if medium._is_remote_before(version):
1962
2533
                    verb, args, search_bytes)
1963
2534
            except errors.UnknownSmartMethod:
1964
2535
                medium._remember_remote_is_before(version)
 
2536
            except errors.UnknownErrorFromSmartServer, e:
 
2537
                if isinstance(search, graph.EverythingResult):
 
2538
                    error_verb = e.error_from_smart_server.error_verb
 
2539
                    if error_verb == 'BadSearch':
 
2540
                        # Pre-2.4 servers don't support this sort of search.
 
2541
                        # XXX: perhaps falling back to VFS on BadSearch is a
 
2542
                        # good idea in general?  It might provide a little bit
 
2543
                        # of protection against client-side bugs.
 
2544
                        medium._remember_remote_is_before((2, 4))
 
2545
                        break
 
2546
                raise
1965
2547
            else:
1966
2548
                response_tuple, response_handler = response
1967
2549
                found_verb = True
1971
2553
        if response_tuple[0] != 'ok':
1972
2554
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1973
2555
        byte_stream = response_handler.read_streamed_body()
1974
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
 
2556
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
 
2557
            self._record_counter)
1975
2558
        if src_format.network_name() != repo._format.network_name():
1976
2559
            raise AssertionError(
1977
2560
                "Mismatched RemoteRepository and stream src %r, %r" % (
2049
2632
 
2050
2633
    def _ensure_real(self):
2051
2634
        if self._custom_format is None:
2052
 
            self._custom_format = branch.network_format_registry.get(
2053
 
                self._network_name)
 
2635
            try:
 
2636
                self._custom_format = branch.network_format_registry.get(
 
2637
                    self._network_name)
 
2638
            except KeyError:
 
2639
                raise errors.UnknownFormatError(kind='branch',
 
2640
                    format=self._network_name)
2054
2641
 
2055
2642
    def get_format_description(self):
2056
2643
        self._ensure_real()
2063
2650
        return a_bzrdir.open_branch(name=name, 
2064
2651
            ignore_fallbacks=ignore_fallbacks)
2065
2652
 
2066
 
    def _vfs_initialize(self, a_bzrdir, name):
 
2653
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
2067
2654
        # Initialisation when using a local bzrdir object, or a non-vfs init
2068
2655
        # method is not available on the server.
2069
2656
        # self._custom_format is always set - the start of initialize ensures
2071
2658
        if isinstance(a_bzrdir, RemoteBzrDir):
2072
2659
            a_bzrdir._ensure_real()
2073
2660
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2074
 
                name)
 
2661
                name, append_revisions_only=append_revisions_only)
2075
2662
        else:
2076
2663
            # We assume the bzrdir is parameterised; it may not be.
2077
 
            result = self._custom_format.initialize(a_bzrdir, name)
 
2664
            result = self._custom_format.initialize(a_bzrdir, name,
 
2665
                append_revisions_only=append_revisions_only)
2078
2666
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2079
2667
            not isinstance(result, RemoteBranch)):
2080
2668
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2081
2669
                                  name=name)
2082
2670
        return result
2083
2671
 
2084
 
    def initialize(self, a_bzrdir, name=None):
 
2672
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2673
                   append_revisions_only=None):
2085
2674
        # 1) get the network name to use.
2086
2675
        if self._custom_format:
2087
2676
            network_name = self._custom_format.network_name()
2088
2677
        else:
2089
2678
            # Select the current bzrlib default and ask for that.
2090
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
2679
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
2091
2680
            reference_format = reference_bzrdir_format.get_branch_format()
2092
2681
            self._custom_format = reference_format
2093
2682
            network_name = reference_format.network_name()
2094
2683
        # Being asked to create on a non RemoteBzrDir:
2095
2684
        if not isinstance(a_bzrdir, RemoteBzrDir):
2096
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2685
            return self._vfs_initialize(a_bzrdir, name=name,
 
2686
                append_revisions_only=append_revisions_only)
2097
2687
        medium = a_bzrdir._client._medium
2098
2688
        if medium._is_remote_before((1, 13)):
2099
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2689
            return self._vfs_initialize(a_bzrdir, name=name,
 
2690
                append_revisions_only=append_revisions_only)
2100
2691
        # Creating on a remote bzr dir.
2101
2692
        # 2) try direct creation via RPC
2102
2693
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2109
2700
        except errors.UnknownSmartMethod:
2110
2701
            # Fallback - use vfs methods
2111
2702
            medium._remember_remote_is_before((1, 13))
2112
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2703
            return self._vfs_initialize(a_bzrdir, name=name,
 
2704
                    append_revisions_only=append_revisions_only)
2113
2705
        if response[0] != 'ok':
2114
2706
            raise errors.UnexpectedSmartServerResponse(response)
2115
2707
        # Turn the response into a RemoteRepository object.
2116
2708
        format = RemoteBranchFormat(network_name=response[1])
2117
2709
        repo_format = response_tuple_to_repo_format(response[3:])
2118
 
        if response[2] == '':
2119
 
            repo_bzrdir = a_bzrdir
 
2710
        repo_path = response[2]
 
2711
        if repository is not None:
 
2712
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
 
2713
            url_diff = urlutils.relative_url(repository.user_url,
 
2714
                    remote_repo_url)
 
2715
            if url_diff != '.':
 
2716
                raise AssertionError(
 
2717
                    'repository.user_url %r does not match URL from server '
 
2718
                    'response (%r + %r)'
 
2719
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
 
2720
            remote_repo = repository
2120
2721
        else:
2121
 
            repo_bzrdir = RemoteBzrDir(
2122
 
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2123
 
                a_bzrdir._client)
2124
 
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
2722
            if repo_path == '':
 
2723
                repo_bzrdir = a_bzrdir
 
2724
            else:
 
2725
                repo_bzrdir = RemoteBzrDir(
 
2726
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
 
2727
                    a_bzrdir._client)
 
2728
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2125
2729
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2126
2730
            format=format, setup_stacking=False, name=name)
 
2731
        if append_revisions_only:
 
2732
            remote_branch.set_append_revisions_only(append_revisions_only)
2127
2733
        # XXX: We know this is a new branch, so it must have revno 0, revid
2128
2734
        # NULL_REVISION. Creating the branch locked would make this be unable
2129
2735
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
2754
        self._ensure_real()
2149
2755
        return self._custom_format.supports_set_append_revisions_only()
2150
2756
 
 
2757
    def _use_default_local_heads_to_fetch(self):
 
2758
        # If the branch format is a metadir format *and* its heads_to_fetch
 
2759
        # implementation is not overridden vs the base class, we can use the
 
2760
        # base class logic rather than use the heads_to_fetch RPC.  This is
 
2761
        # usually cheaper in terms of net round trips, as the last-revision and
 
2762
        # tags info fetched is cached and would be fetched anyway.
 
2763
        self._ensure_real()
 
2764
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
 
2765
            branch_class = self._custom_format._branch_class()
 
2766
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
 
2767
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
 
2768
                return True
 
2769
        return False
 
2770
 
 
2771
 
 
2772
class RemoteBranchStore(config.IniFileStore):
 
2773
    """Branch store which attempts to use HPSS calls to retrieve branch store.
 
2774
 
 
2775
    Note that this is specific to bzr-based formats.
 
2776
    """
 
2777
 
 
2778
    def __init__(self, branch):
 
2779
        super(RemoteBranchStore, self).__init__()
 
2780
        self.branch = branch
 
2781
        self.id = "branch"
 
2782
        self._real_store = None
 
2783
 
 
2784
    def lock_write(self, token=None):
 
2785
        return self.branch.lock_write(token)
 
2786
 
 
2787
    def unlock(self):
 
2788
        return self.branch.unlock()
 
2789
 
 
2790
    @needs_write_lock
 
2791
    def save(self):
 
2792
        # We need to be able to override the undecorated implementation
 
2793
        self.save_without_locking()
 
2794
 
 
2795
    def save_without_locking(self):
 
2796
        super(RemoteBranchStore, self).save()
 
2797
 
 
2798
    def external_url(self):
 
2799
        return self.branch.user_url
 
2800
 
 
2801
    def _load_content(self):
 
2802
        path = self.branch._remote_path()
 
2803
        try:
 
2804
            response, handler = self.branch._call_expecting_body(
 
2805
                'Branch.get_config_file', path)
 
2806
        except errors.UnknownSmartMethod:
 
2807
            self._ensure_real()
 
2808
            return self._real_store._load_content()
 
2809
        if len(response) and response[0] != 'ok':
 
2810
            raise errors.UnexpectedSmartServerResponse(response)
 
2811
        return handler.read_body_bytes()
 
2812
 
 
2813
    def _save_content(self, content):
 
2814
        path = self.branch._remote_path()
 
2815
        try:
 
2816
            response, handler = self.branch._call_with_body_bytes_expecting_body(
 
2817
                'Branch.put_config_file', (path,
 
2818
                    self.branch._lock_token, self.branch._repo_lock_token),
 
2819
                content)
 
2820
        except errors.UnknownSmartMethod:
 
2821
            self._ensure_real()
 
2822
            return self._real_store._save_content(content)
 
2823
        handler.cancel_read_body()
 
2824
        if response != ('ok', ):
 
2825
            raise errors.UnexpectedSmartServerResponse(response)
 
2826
 
 
2827
    def _ensure_real(self):
 
2828
        self.branch._ensure_real()
 
2829
        if self._real_store is None:
 
2830
            self._real_store = config.BranchStore(self.branch)
 
2831
 
2151
2832
 
2152
2833
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
2834
    """Branch stored on a server accessed by HPSS RPC.
2156
2837
    """
2157
2838
 
2158
2839
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2159
 
        _client=None, format=None, setup_stacking=True, name=None):
 
2840
        _client=None, format=None, setup_stacking=True, name=None,
 
2841
        possible_transports=None):
2160
2842
        """Create a RemoteBranch instance.
2161
2843
 
2162
2844
        :param real_branch: An optional local implementation of the branch
2227
2909
            hook(self)
2228
2910
        self._is_stacked = False
2229
2911
        if setup_stacking:
2230
 
            self._setup_stacking()
 
2912
            self._setup_stacking(possible_transports)
2231
2913
 
2232
 
    def _setup_stacking(self):
 
2914
    def _setup_stacking(self, possible_transports):
2233
2915
        # configure stacking into the remote repository, by reading it from
2234
2916
        # the vfs branch.
2235
2917
        try:
2238
2920
            errors.UnstackableRepositoryFormat), e:
2239
2921
            return
2240
2922
        self._is_stacked = True
2241
 
        self._activate_fallback_location(fallback_url)
 
2923
        if possible_transports is None:
 
2924
            possible_transports = []
 
2925
        else:
 
2926
            possible_transports = list(possible_transports)
 
2927
        possible_transports.append(self.bzrdir.root_transport)
 
2928
        self._activate_fallback_location(fallback_url,
 
2929
            possible_transports=possible_transports)
2242
2930
 
2243
2931
    def _get_config(self):
2244
2932
        return RemoteBranchConfig(self)
2245
2933
 
 
2934
    def _get_config_store(self):
 
2935
        return RemoteBranchStore(self)
 
2936
 
2246
2937
    def _get_real_transport(self):
2247
2938
        # if we try vfs access, return the real branch's vfs transport
2248
2939
        self._ensure_real()
2311
3002
                self.bzrdir, self._client)
2312
3003
        return self._control_files
2313
3004
 
2314
 
    def _get_checkout_format(self):
 
3005
    def _get_checkout_format(self, lightweight=False):
2315
3006
        self._ensure_real()
2316
 
        return self._real_branch._get_checkout_format()
 
3007
        if lightweight:
 
3008
            format = RemoteBzrDirFormat()
 
3009
            self.bzrdir._format._supply_sub_formats_to(format)
 
3010
            format.workingtree_format = self._real_branch._get_checkout_format(
 
3011
                lightweight=lightweight).workingtree_format
 
3012
            return format
 
3013
        else:
 
3014
            return self._real_branch._get_checkout_format(lightweight=False)
2317
3015
 
2318
3016
    def get_physical_lock_status(self):
2319
3017
        """See Branch.get_physical_lock_status()."""
2320
 
        # should be an API call to the server, as branches must be lockable.
2321
 
        self._ensure_real()
2322
 
        return self._real_branch.get_physical_lock_status()
 
3018
        try:
 
3019
            response = self._client.call('Branch.get_physical_lock_status',
 
3020
                self._remote_path())
 
3021
        except errors.UnknownSmartMethod:
 
3022
            self._ensure_real()
 
3023
            return self._real_branch.get_physical_lock_status()
 
3024
        if response[0] not in ('yes', 'no'):
 
3025
            raise errors.UnexpectedSmartServerResponse(response)
 
3026
        return (response[0] == 'yes')
2323
3027
 
2324
3028
    def get_stacked_on_url(self):
2325
3029
        """Get the URL this branch is stacked against.
2352
3056
            self._is_stacked = False
2353
3057
        else:
2354
3058
            self._is_stacked = True
2355
 
        
 
3059
 
2356
3060
    def _vfs_get_tags_bytes(self):
2357
3061
        self._ensure_real()
2358
3062
        return self._real_branch._get_tags_bytes()
2359
3063
 
 
3064
    @needs_read_lock
2360
3065
    def _get_tags_bytes(self):
 
3066
        if self._tags_bytes is None:
 
3067
            self._tags_bytes = self._get_tags_bytes_via_hpss()
 
3068
        return self._tags_bytes
 
3069
 
 
3070
    def _get_tags_bytes_via_hpss(self):
2361
3071
        medium = self._client._medium
2362
3072
        if medium._is_remote_before((1, 13)):
2363
3073
            return self._vfs_get_tags_bytes()
2373
3083
        return self._real_branch._set_tags_bytes(bytes)
2374
3084
 
2375
3085
    def _set_tags_bytes(self, bytes):
 
3086
        if self.is_locked():
 
3087
            self._tags_bytes = bytes
2376
3088
        medium = self._client._medium
2377
3089
        if medium._is_remote_before((1, 18)):
2378
3090
            self._vfs_set_tags_bytes(bytes)
2387
3099
            self._vfs_set_tags_bytes(bytes)
2388
3100
 
2389
3101
    def lock_read(self):
 
3102
        """Lock the branch for read operations.
 
3103
 
 
3104
        :return: A bzrlib.lock.LogicalLockResult.
 
3105
        """
2390
3106
        self.repository.lock_read()
2391
3107
        if not self._lock_mode:
2392
3108
            self._note_lock('r')
2396
3112
                self._real_branch.lock_read()
2397
3113
        else:
2398
3114
            self._lock_count += 1
 
3115
        return lock.LogicalLockResult(self.unlock)
2399
3116
 
2400
3117
    def _remote_lock_write(self, token):
2401
3118
        if token is None:
2402
3119
            branch_token = repo_token = ''
2403
3120
        else:
2404
3121
            branch_token = token
2405
 
            repo_token = self.repository.lock_write()
 
3122
            repo_token = self.repository.lock_write().repository_token
2406
3123
            self.repository.unlock()
2407
3124
        err_context = {'token': token}
2408
 
        response = self._call(
2409
 
            'Branch.lock_write', self._remote_path(), branch_token,
2410
 
            repo_token or '', **err_context)
 
3125
        try:
 
3126
            response = self._call(
 
3127
                'Branch.lock_write', self._remote_path(), branch_token,
 
3128
                repo_token or '', **err_context)
 
3129
        except errors.LockContention, e:
 
3130
            # The LockContention from the server doesn't have any
 
3131
            # information about the lock_url. We re-raise LockContention
 
3132
            # with valid lock_url.
 
3133
            raise errors.LockContention('(remote lock)',
 
3134
                self.repository.base.split('.bzr/')[0])
2411
3135
        if response[0] != 'ok':
2412
3136
            raise errors.UnexpectedSmartServerResponse(response)
2413
3137
        ok, branch_token, repo_token = response
2434
3158
            self._lock_mode = 'w'
2435
3159
            self._lock_count = 1
2436
3160
        elif self._lock_mode == 'r':
2437
 
            raise errors.ReadOnlyTransaction
 
3161
            raise errors.ReadOnlyError(self)
2438
3162
        else:
2439
3163
            if token is not None:
2440
3164
                # A token was given to lock_write, and we're relocking, so
2445
3169
            self._lock_count += 1
2446
3170
            # Re-lock the repository too.
2447
3171
            self.repository.lock_write(self._repo_lock_token)
2448
 
        return self._lock_token or None
 
3172
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
2449
3173
 
2450
3174
    def _unlock(self, branch_token, repo_token):
2451
3175
        err_context = {'token': str((branch_token, repo_token))}
2489
3213
            self.repository.unlock()
2490
3214
 
2491
3215
    def break_lock(self):
2492
 
        self._ensure_real()
2493
 
        return self._real_branch.break_lock()
 
3216
        try:
 
3217
            response = self._call(
 
3218
                'Branch.break_lock', self._remote_path())
 
3219
        except errors.UnknownSmartMethod:
 
3220
            self._ensure_real()
 
3221
            return self._real_branch.break_lock()
 
3222
        if response != ('ok',):
 
3223
            raise errors.UnexpectedSmartServerResponse(response)
2494
3224
 
2495
3225
    def leave_lock_in_place(self):
2496
3226
        if not self._lock_token:
2520
3250
            missing_parent = parent_map[missing_parent]
2521
3251
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2522
3252
 
2523
 
    def _last_revision_info(self):
 
3253
    def _read_last_revision_info(self):
2524
3254
        response = self._call('Branch.last_revision_info', self._remote_path())
2525
3255
        if response[0] != 'ok':
2526
3256
            raise SmartProtocolError('unexpected response code %s' % (response,))
2589
3319
            raise errors.UnexpectedSmartServerResponse(response)
2590
3320
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2591
3321
 
 
3322
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2592
3323
    @needs_write_lock
2593
3324
    def set_revision_history(self, rev_history):
 
3325
        """See Branch.set_revision_history."""
 
3326
        self._set_revision_history(rev_history)
 
3327
 
 
3328
    @needs_write_lock
 
3329
    def _set_revision_history(self, rev_history):
2594
3330
        # Send just the tip revision of the history; the server will generate
2595
3331
        # the full history from that.  If the revision doesn't exist in this
2596
3332
        # branch, NoSuchRevision will be raised.
2654
3390
            _override_hook_target=self, **kwargs)
2655
3391
 
2656
3392
    @needs_read_lock
2657
 
    def push(self, target, overwrite=False, stop_revision=None):
 
3393
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
2658
3394
        self._ensure_real()
2659
3395
        return self._real_branch.push(
2660
 
            target, overwrite=overwrite, stop_revision=stop_revision,
 
3396
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
2661
3397
            _override_hook_source_branch=self)
2662
3398
 
2663
3399
    def is_locked(self):
2664
3400
        return self._lock_count >= 1
2665
3401
 
2666
3402
    @needs_read_lock
 
3403
    def revision_id_to_dotted_revno(self, revision_id):
 
3404
        """Given a revision id, return its dotted revno.
 
3405
 
 
3406
        :return: a tuple like (1,) or (400,1,3).
 
3407
        """
 
3408
        try:
 
3409
            response = self._call('Branch.revision_id_to_revno',
 
3410
                self._remote_path(), revision_id)
 
3411
        except errors.UnknownSmartMethod:
 
3412
            self._ensure_real()
 
3413
            return self._real_branch.revision_id_to_dotted_revno(revision_id)
 
3414
        if response[0] == 'ok':
 
3415
            return tuple([int(x) for x in response[1:]])
 
3416
        else:
 
3417
            raise errors.UnexpectedSmartServerResponse(response)
 
3418
 
 
3419
    @needs_read_lock
2667
3420
    def revision_id_to_revno(self, revision_id):
2668
 
        self._ensure_real()
2669
 
        return self._real_branch.revision_id_to_revno(revision_id)
 
3421
        """Given a revision id on the branch mainline, return its revno.
 
3422
 
 
3423
        :return: an integer
 
3424
        """
 
3425
        try:
 
3426
            response = self._call('Branch.revision_id_to_revno',
 
3427
                self._remote_path(), revision_id)
 
3428
        except errors.UnknownSmartMethod:
 
3429
            self._ensure_real()
 
3430
            return self._real_branch.revision_id_to_revno(revision_id)
 
3431
        if response[0] == 'ok':
 
3432
            if len(response) == 2:
 
3433
                return int(response[1])
 
3434
            raise NoSuchRevision(self, revision_id)
 
3435
        else:
 
3436
            raise errors.UnexpectedSmartServerResponse(response)
2670
3437
 
2671
3438
    @needs_write_lock
2672
3439
    def set_last_revision_info(self, revno, revision_id):
2673
3440
        # XXX: These should be returned by the set_last_revision_info verb
2674
3441
        old_revno, old_revid = self.last_revision_info()
2675
3442
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
 
        revision_id = ensure_null(revision_id)
 
3443
        if not revision_id or not isinstance(revision_id, basestring):
 
3444
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2677
3445
        try:
2678
3446
            response = self._call('Branch.set_last_revision_info',
2679
3447
                self._remote_path(), self._lock_token, self._repo_lock_token,
2708
3476
            except errors.UnknownSmartMethod:
2709
3477
                medium._remember_remote_is_before((1, 6))
2710
3478
        self._clear_cached_state_of_remote_branch_only()
2711
 
        self.set_revision_history(self._lefthand_history(revision_id,
 
3479
        self._set_revision_history(self._lefthand_history(revision_id,
2712
3480
            last_rev=last_rev,other_branch=other_branch))
2713
3481
 
2714
3482
    def set_push_location(self, location):
2715
3483
        self._ensure_real()
2716
3484
        return self._real_branch.set_push_location(location)
2717
3485
 
 
3486
    def heads_to_fetch(self):
 
3487
        if self._format._use_default_local_heads_to_fetch():
 
3488
            # We recognise this format, and its heads-to-fetch implementation
 
3489
            # is the default one (tip + tags).  In this case it's cheaper to
 
3490
            # just use the default implementation rather than a special RPC as
 
3491
            # the tip and tags data is cached.
 
3492
            return branch.Branch.heads_to_fetch(self)
 
3493
        medium = self._client._medium
 
3494
        if medium._is_remote_before((2, 4)):
 
3495
            return self._vfs_heads_to_fetch()
 
3496
        try:
 
3497
            return self._rpc_heads_to_fetch()
 
3498
        except errors.UnknownSmartMethod:
 
3499
            medium._remember_remote_is_before((2, 4))
 
3500
            return self._vfs_heads_to_fetch()
 
3501
 
 
3502
    def _rpc_heads_to_fetch(self):
 
3503
        response = self._call('Branch.heads_to_fetch', self._remote_path())
 
3504
        if len(response) != 2:
 
3505
            raise errors.UnexpectedSmartServerResponse(response)
 
3506
        must_fetch, if_present_fetch = response
 
3507
        return set(must_fetch), set(if_present_fetch)
 
3508
 
 
3509
    def _vfs_heads_to_fetch(self):
 
3510
        self._ensure_real()
 
3511
        return self._real_branch.heads_to_fetch()
 
3512
 
2718
3513
 
2719
3514
class RemoteConfig(object):
2720
3515
    """A Config that reads and writes from smart verbs.
2734
3529
        """
2735
3530
        try:
2736
3531
            configobj = self._get_configobj()
 
3532
            section_obj = None
2737
3533
            if section is None:
2738
3534
                section_obj = configobj
2739
3535
            else:
2740
3536
                try:
2741
3537
                    section_obj = configobj[section]
2742
3538
                except KeyError:
2743
 
                    return default
2744
 
            return section_obj.get(name, default)
 
3539
                    pass
 
3540
            if section_obj is None:
 
3541
                value = default
 
3542
            else:
 
3543
                value = section_obj.get(name, default)
2745
3544
        except errors.UnknownSmartMethod:
2746
 
            return self._vfs_get_option(name, section, default)
 
3545
            value = self._vfs_get_option(name, section, default)
 
3546
        for hook in config.OldConfigHooks['get']:
 
3547
            hook(self, name, value)
 
3548
        return value
2747
3549
 
2748
3550
    def _response_to_configobj(self, response):
2749
3551
        if len(response[0]) and response[0][0] != 'ok':
2750
3552
            raise errors.UnexpectedSmartServerResponse(response)
2751
3553
        lines = response[1].read_body_bytes().splitlines()
2752
 
        return config.ConfigObj(lines, encoding='utf-8')
 
3554
        conf = config.ConfigObj(lines, encoding='utf-8')
 
3555
        for hook in config.OldConfigHooks['load']:
 
3556
            hook(self)
 
3557
        return conf
2753
3558
 
2754
3559
 
2755
3560
class RemoteBranchConfig(RemoteConfig):
2774
3579
        medium = self._branch._client._medium
2775
3580
        if medium._is_remote_before((1, 14)):
2776
3581
            return self._vfs_set_option(value, name, section)
 
3582
        if isinstance(value, dict):
 
3583
            if medium._is_remote_before((2, 2)):
 
3584
                return self._vfs_set_option(value, name, section)
 
3585
            return self._set_config_option_dict(value, name, section)
 
3586
        else:
 
3587
            return self._set_config_option(value, name, section)
 
3588
 
 
3589
    def _set_config_option(self, value, name, section):
2777
3590
        try:
2778
3591
            path = self._branch._remote_path()
2779
3592
            response = self._branch._client.call('Branch.set_config_option',
2780
3593
                path, self._branch._lock_token, self._branch._repo_lock_token,
2781
3594
                value.encode('utf8'), name, section or '')
2782
3595
        except errors.UnknownSmartMethod:
 
3596
            medium = self._branch._client._medium
2783
3597
            medium._remember_remote_is_before((1, 14))
2784
3598
            return self._vfs_set_option(value, name, section)
2785
3599
        if response != ():
2786
3600
            raise errors.UnexpectedSmartServerResponse(response)
2787
3601
 
 
3602
    def _serialize_option_dict(self, option_dict):
 
3603
        utf8_dict = {}
 
3604
        for key, value in option_dict.items():
 
3605
            if isinstance(key, unicode):
 
3606
                key = key.encode('utf8')
 
3607
            if isinstance(value, unicode):
 
3608
                value = value.encode('utf8')
 
3609
            utf8_dict[key] = value
 
3610
        return bencode.bencode(utf8_dict)
 
3611
 
 
3612
    def _set_config_option_dict(self, value, name, section):
 
3613
        try:
 
3614
            path = self._branch._remote_path()
 
3615
            serialised_dict = self._serialize_option_dict(value)
 
3616
            response = self._branch._client.call(
 
3617
                'Branch.set_config_option_dict',
 
3618
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
3619
                serialised_dict, name, section or '')
 
3620
        except errors.UnknownSmartMethod:
 
3621
            medium = self._branch._client._medium
 
3622
            medium._remember_remote_is_before((2, 2))
 
3623
            return self._vfs_set_option(value, name, section)
 
3624
        if response != ():
 
3625
            raise errors.UnexpectedSmartServerResponse(response)
 
3626
 
2788
3627
    def _real_object(self):
2789
3628
        self._branch._ensure_real()
2790
3629
        return self._branch._real_branch
2839
3678
        tar.extract(tarinfo, to_dir)
2840
3679
 
2841
3680
 
 
3681
error_translators = registry.Registry()
 
3682
no_context_error_translators = registry.Registry()
 
3683
 
 
3684
 
2842
3685
def _translate_error(err, **context):
2843
3686
    """Translate an ErrorFromSmartServer into a more useful error.
2844
3687
 
2873
3716
                    'Missing key %r in context %r', key_err.args[0], context)
2874
3717
                raise err
2875
3718
 
2876
 
    if err.error_verb == 'IncompatibleRepositories':
2877
 
        raise errors.IncompatibleRepositories(err.error_args[0],
2878
 
            err.error_args[1], err.error_args[2])
2879
 
    elif err.error_verb == 'NoSuchRevision':
2880
 
        raise NoSuchRevision(find('branch'), err.error_args[0])
2881
 
    elif err.error_verb == 'nosuchrevision':
2882
 
        raise NoSuchRevision(find('repository'), err.error_args[0])
2883
 
    elif err.error_verb == 'nobranch':
2884
 
        if len(err.error_args) >= 1:
2885
 
            extra = err.error_args[0]
2886
 
        else:
2887
 
            extra = None
2888
 
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2889
 
            detail=extra)
2890
 
    elif err.error_verb == 'norepository':
2891
 
        raise errors.NoRepositoryPresent(find('bzrdir'))
2892
 
    elif err.error_verb == 'LockContention':
2893
 
        raise errors.LockContention('(remote lock)')
2894
 
    elif err.error_verb == 'UnlockableTransport':
2895
 
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
2896
 
    elif err.error_verb == 'LockFailed':
2897
 
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
2898
 
    elif err.error_verb == 'TokenMismatch':
2899
 
        raise errors.TokenMismatch(find('token'), '(remote token)')
2900
 
    elif err.error_verb == 'Diverged':
2901
 
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
2902
 
    elif err.error_verb == 'TipChangeRejected':
2903
 
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2904
 
    elif err.error_verb == 'UnstackableBranchFormat':
2905
 
        raise errors.UnstackableBranchFormat(*err.error_args)
2906
 
    elif err.error_verb == 'UnstackableRepositoryFormat':
2907
 
        raise errors.UnstackableRepositoryFormat(*err.error_args)
2908
 
    elif err.error_verb == 'NotStacked':
2909
 
        raise errors.NotStacked(branch=find('branch'))
2910
 
    elif err.error_verb == 'PermissionDenied':
2911
 
        path = get_path()
2912
 
        if len(err.error_args) >= 2:
2913
 
            extra = err.error_args[1]
2914
 
        else:
2915
 
            extra = None
2916
 
        raise errors.PermissionDenied(path, extra=extra)
2917
 
    elif err.error_verb == 'ReadError':
2918
 
        path = get_path()
2919
 
        raise errors.ReadError(path)
2920
 
    elif err.error_verb == 'NoSuchFile':
2921
 
        path = get_path()
2922
 
        raise errors.NoSuchFile(path)
2923
 
    elif err.error_verb == 'FileExists':
2924
 
        raise errors.FileExists(err.error_args[0])
2925
 
    elif err.error_verb == 'DirectoryNotEmpty':
2926
 
        raise errors.DirectoryNotEmpty(err.error_args[0])
2927
 
    elif err.error_verb == 'ShortReadvError':
2928
 
        args = err.error_args
2929
 
        raise errors.ShortReadvError(
2930
 
            args[0], int(args[1]), int(args[2]), int(args[3]))
2931
 
    elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
 
3719
    try:
 
3720
        translator = error_translators.get(err.error_verb)
 
3721
    except KeyError:
 
3722
        pass
 
3723
    else:
 
3724
        raise translator(err, find, get_path)
 
3725
    try:
 
3726
        translator = no_context_error_translators.get(err.error_verb)
 
3727
    except KeyError:
 
3728
        raise errors.UnknownErrorFromSmartServer(err)
 
3729
    else:
 
3730
        raise translator(err)
 
3731
 
 
3732
 
 
3733
error_translators.register('NoSuchRevision',
 
3734
    lambda err, find, get_path: NoSuchRevision(
 
3735
        find('branch'), err.error_args[0]))
 
3736
error_translators.register('nosuchrevision',
 
3737
    lambda err, find, get_path: NoSuchRevision(
 
3738
        find('repository'), err.error_args[0]))
 
3739
 
 
3740
def _translate_nobranch_error(err, find, get_path):
 
3741
    if len(err.error_args) >= 1:
 
3742
        extra = err.error_args[0]
 
3743
    else:
 
3744
        extra = None
 
3745
    return errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
3746
        detail=extra)
 
3747
 
 
3748
error_translators.register('nobranch', _translate_nobranch_error)
 
3749
error_translators.register('norepository',
 
3750
    lambda err, find, get_path: errors.NoRepositoryPresent(
 
3751
        find('bzrdir')))
 
3752
error_translators.register('UnlockableTransport',
 
3753
    lambda err, find, get_path: errors.UnlockableTransport(
 
3754
        find('bzrdir').root_transport))
 
3755
error_translators.register('TokenMismatch',
 
3756
    lambda err, find, get_path: errors.TokenMismatch(
 
3757
        find('token'), '(remote token)'))
 
3758
error_translators.register('Diverged',
 
3759
    lambda err, find, get_path: errors.DivergedBranches(
 
3760
        find('branch'), find('other_branch')))
 
3761
error_translators.register('NotStacked',
 
3762
    lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
 
3763
 
 
3764
def _translate_PermissionDenied(err, find, get_path):
 
3765
    path = get_path()
 
3766
    if len(err.error_args) >= 2:
 
3767
        extra = err.error_args[1]
 
3768
    else:
 
3769
        extra = None
 
3770
    return errors.PermissionDenied(path, extra=extra)
 
3771
 
 
3772
error_translators.register('PermissionDenied', _translate_PermissionDenied)
 
3773
error_translators.register('ReadError',
 
3774
    lambda err, find, get_path: errors.ReadError(get_path()))
 
3775
error_translators.register('NoSuchFile',
 
3776
    lambda err, find, get_path: errors.NoSuchFile(get_path()))
 
3777
no_context_error_translators.register('IncompatibleRepositories',
 
3778
    lambda err: errors.IncompatibleRepositories(
 
3779
        err.error_args[0], err.error_args[1], err.error_args[2]))
 
3780
no_context_error_translators.register('LockContention',
 
3781
    lambda err: errors.LockContention('(remote lock)'))
 
3782
no_context_error_translators.register('LockFailed',
 
3783
    lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
 
3784
no_context_error_translators.register('TipChangeRejected',
 
3785
    lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
 
3786
no_context_error_translators.register('UnstackableBranchFormat',
 
3787
    lambda err: errors.UnstackableBranchFormat(*err.error_args))
 
3788
no_context_error_translators.register('UnstackableRepositoryFormat',
 
3789
    lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
 
3790
no_context_error_translators.register('FileExists',
 
3791
    lambda err: errors.FileExists(err.error_args[0]))
 
3792
no_context_error_translators.register('DirectoryNotEmpty',
 
3793
    lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
 
3794
 
 
3795
def _translate_short_readv_error(err):
 
3796
    args = err.error_args
 
3797
    return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
 
3798
        int(args[3]))
 
3799
 
 
3800
no_context_error_translators.register('ShortReadvError',
 
3801
    _translate_short_readv_error)
 
3802
 
 
3803
def _translate_unicode_error(err):
2932
3804
        encoding = str(err.error_args[0]) # encoding must always be a string
2933
3805
        val = err.error_args[1]
2934
3806
        start = int(err.error_args[2])
2942
3814
            raise UnicodeDecodeError(encoding, val, start, end, reason)
2943
3815
        elif err.error_verb == 'UnicodeEncodeError':
2944
3816
            raise UnicodeEncodeError(encoding, val, start, end, reason)
2945
 
    elif err.error_verb == 'ReadOnlyError':
2946
 
        raise errors.TransportNotPossible('readonly transport')
2947
 
    raise errors.UnknownErrorFromSmartServer(err)
 
3817
 
 
3818
no_context_error_translators.register('UnicodeEncodeError',
 
3819
    _translate_unicode_error)
 
3820
no_context_error_translators.register('UnicodeDecodeError',
 
3821
    _translate_unicode_error)
 
3822
no_context_error_translators.register('ReadOnlyError',
 
3823
    lambda err: errors.TransportNotPossible('readonly transport'))
 
3824
no_context_error_translators.register('MemoryError',
 
3825
    lambda err: errors.BzrError("remote server out of memory\n"
 
3826
        "Retry non-remotely, or contact the server admin for details."))
 
3827
 
 
3828
no_context_error_translators.register('BzrCheckError',
 
3829
    lambda err: errors.BzrCheckError(msg=err.error_args[0]))
 
3830
 
 
3831
error_translators.register('UnsuspendableWriteGroup',
 
3832
    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
 
3833
        repository=find('repository')))
 
3834
error_translators.register('UnresumableWriteGroup',
 
3835
    lambda err, find, get_path: errors.UnresumableWriteGroup(
 
3836
        repository=find('repository'), write_groups=err.error_args[0],
 
3837
        reason=err.error_args[1]))