/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: Vincent Ladeuil
  • Date: 2011-11-28 11:49:58 UTC
  • mto: This revision was merged to the branch mainline in revision 6321.
  • Revision ID: v.ladeuil+lp@free.fr-20111128114958-ovgttuphf5y0z9xb
Simpler fix for test_smart_server_connection_reset re-using more of the existing test server infrastructure.

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
            real_format = controldir.network_format_registry.get(self._network_name)
 
123
            return 'Remote: ' + real_format.get_format_description()
 
124
        return 'bzr remote bzrdir'
 
125
 
 
126
    def get_format_string(self):
 
127
        raise NotImplementedError(self.get_format_string)
 
128
 
 
129
    def network_name(self):
 
130
        if self._network_name:
 
131
            return self._network_name
 
132
        else:
 
133
            raise AssertionError("No network name set.")
 
134
 
 
135
    def initialize_on_transport(self, transport):
 
136
        try:
 
137
            # hand off the request to the smart server
 
138
            client_medium = transport.get_smart_medium()
 
139
        except errors.NoSmartMedium:
 
140
            # TODO: lookup the local format from a server hint.
 
141
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
142
            return local_dir_format.initialize_on_transport(transport)
 
143
        client = _SmartClient(client_medium)
 
144
        path = client.remote_path_from_transport(transport)
 
145
        try:
 
146
            response = client.call('BzrDirFormat.initialize', path)
 
147
        except errors.ErrorFromSmartServer, err:
 
148
            _translate_error(err, path=path)
 
149
        if response[0] != 'ok':
 
150
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
 
151
        format = RemoteBzrDirFormat()
 
152
        self._supply_sub_formats_to(format)
 
153
        return RemoteBzrDir(transport, format)
 
154
 
 
155
    def parse_NoneTrueFalse(self, arg):
 
156
        if not arg:
 
157
            return None
 
158
        if arg == 'False':
 
159
            return False
 
160
        if arg == 'True':
 
161
            return True
 
162
        raise AssertionError("invalid arg %r" % arg)
 
163
 
 
164
    def _serialize_NoneTrueFalse(self, arg):
 
165
        if arg is False:
 
166
            return 'False'
 
167
        if arg:
 
168
            return 'True'
 
169
        return ''
 
170
 
 
171
    def _serialize_NoneString(self, arg):
 
172
        return arg or ''
 
173
 
 
174
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
175
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
176
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
177
        shared_repo=False):
 
178
        try:
 
179
            # hand off the request to the smart server
 
180
            client_medium = transport.get_smart_medium()
 
181
        except errors.NoSmartMedium:
 
182
            do_vfs = True
 
183
        else:
 
184
            # Decline to open it if the server doesn't support our required
 
185
            # version (3) so that the VFS-based transport will do it.
 
186
            if client_medium.should_probe():
 
187
                try:
 
188
                    server_version = client_medium.protocol_version()
 
189
                    if server_version != '2':
 
190
                        do_vfs = True
 
191
                    else:
 
192
                        do_vfs = False
 
193
                except errors.SmartProtocolError:
 
194
                    # Apparently there's no usable smart server there, even though
 
195
                    # the medium supports the smart protocol.
 
196
                    do_vfs = True
 
197
            else:
 
198
                do_vfs = False
 
199
        if not do_vfs:
 
200
            client = _SmartClient(client_medium)
 
201
            path = client.remote_path_from_transport(transport)
 
202
            if client_medium._is_remote_before((1, 16)):
 
203
                do_vfs = True
 
204
        if do_vfs:
 
205
            # TODO: lookup the local format from a server hint.
 
206
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
207
            self._supply_sub_formats_to(local_dir_format)
 
208
            return local_dir_format.initialize_on_transport_ex(transport,
 
209
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
210
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
211
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
212
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
213
                vfs_only=True)
 
214
        return self._initialize_on_transport_ex_rpc(client, path, transport,
 
215
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
216
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
 
217
 
 
218
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
 
219
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
220
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
 
221
        args = []
 
222
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
 
223
        args.append(self._serialize_NoneTrueFalse(create_prefix))
 
224
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
 
225
        args.append(self._serialize_NoneString(stacked_on))
 
226
        # stack_on_pwd is often/usually our transport
 
227
        if stack_on_pwd:
 
228
            try:
 
229
                stack_on_pwd = transport.relpath(stack_on_pwd)
 
230
                if not stack_on_pwd:
 
231
                    stack_on_pwd = '.'
 
232
            except errors.PathNotChild:
 
233
                pass
 
234
        args.append(self._serialize_NoneString(stack_on_pwd))
 
235
        args.append(self._serialize_NoneString(repo_format_name))
 
236
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
 
237
        args.append(self._serialize_NoneTrueFalse(shared_repo))
 
238
        request_network_name = self._network_name or \
 
239
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
 
240
        try:
 
241
            response = client.call('BzrDirFormat.initialize_ex_1.16',
 
242
                request_network_name, path, *args)
 
243
        except errors.UnknownSmartMethod:
 
244
            client._medium._remember_remote_is_before((1,16))
 
245
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
246
            self._supply_sub_formats_to(local_dir_format)
 
247
            return local_dir_format.initialize_on_transport_ex(transport,
 
248
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
249
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
250
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
251
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
252
                vfs_only=True)
 
253
        except errors.ErrorFromSmartServer, err:
 
254
            _translate_error(err, path=path)
 
255
        repo_path = response[0]
 
256
        bzrdir_name = response[6]
 
257
        require_stacking = response[7]
 
258
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
 
259
        format = RemoteBzrDirFormat()
 
260
        format._network_name = bzrdir_name
 
261
        self._supply_sub_formats_to(format)
 
262
        bzrdir = RemoteBzrDir(transport, format, _client=client)
 
263
        if repo_path:
 
264
            repo_format = response_tuple_to_repo_format(response[1:])
 
265
            if repo_path == '.':
 
266
                repo_path = ''
 
267
            if repo_path:
 
268
                repo_bzrdir_format = RemoteBzrDirFormat()
 
269
                repo_bzrdir_format._network_name = response[5]
 
270
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
 
271
                    repo_bzrdir_format)
 
272
            else:
 
273
                repo_bzr = bzrdir
 
274
            final_stack = response[8] or None
 
275
            final_stack_pwd = response[9] or None
 
276
            if final_stack_pwd:
 
277
                final_stack_pwd = urlutils.join(
 
278
                    transport.base, final_stack_pwd)
 
279
            remote_repo = RemoteRepository(repo_bzr, repo_format)
 
280
            if len(response) > 10:
 
281
                # Updated server verb that locks remotely.
 
282
                repo_lock_token = response[10] or None
 
283
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
 
284
                if repo_lock_token:
 
285
                    remote_repo.dont_leave_lock_in_place()
 
286
            else:
 
287
                remote_repo.lock_write()
 
288
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
 
289
                final_stack_pwd, require_stacking)
 
290
            policy.acquire_repository()
 
291
        else:
 
292
            remote_repo = None
 
293
            policy = None
 
294
        bzrdir._format.set_branch_format(self.get_branch_format())
 
295
        if require_stacking:
 
296
            # The repo has already been created, but we need to make sure that
 
297
            # we'll make a stackable branch.
 
298
            bzrdir._format.require_stacking(_skip_repo=True)
 
299
        return remote_repo, bzrdir, require_stacking, policy
 
300
 
 
301
    def _open(self, transport):
 
302
        return RemoteBzrDir(transport, self)
 
303
 
 
304
    def __eq__(self, other):
 
305
        if not isinstance(other, RemoteBzrDirFormat):
 
306
            return False
 
307
        return self.get_format_description() == other.get_format_description()
 
308
 
 
309
    def __return_repository_format(self):
 
310
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
 
311
        # repository format has been asked for, tell the RemoteRepositoryFormat
 
312
        # that it should use that for init() etc.
 
313
        result = RemoteRepositoryFormat()
 
314
        custom_format = getattr(self, '_repository_format', None)
 
315
        if custom_format:
 
316
            if isinstance(custom_format, RemoteRepositoryFormat):
 
317
                return custom_format
 
318
            else:
 
319
                # We will use the custom format to create repositories over the
 
320
                # wire; expose its details like rich_root_data for code to
 
321
                # query
 
322
                result._custom_format = custom_format
 
323
        return result
 
324
 
 
325
    def get_branch_format(self):
 
326
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
 
327
        if not isinstance(result, RemoteBranchFormat):
 
328
            new_result = RemoteBranchFormat()
 
329
            new_result._custom_format = result
 
330
            # cache the result
 
331
            self.set_branch_format(new_result)
 
332
            result = new_result
 
333
        return result
 
334
 
 
335
    repository_format = property(__return_repository_format,
 
336
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
 
337
 
 
338
 
 
339
class RemoteControlStore(config.IniFileStore):
 
340
    """Control store which attempts to use HPSS calls to retrieve control store.
 
341
 
 
342
    Note that this is specific to bzr-based formats.
 
343
    """
 
344
 
 
345
    def __init__(self, bzrdir):
 
346
        super(RemoteControlStore, self).__init__()
 
347
        self.bzrdir = bzrdir
 
348
        self._real_store = None
 
349
 
 
350
    def lock_write(self, token=None):
 
351
        self._ensure_real()
 
352
        return self._real_store.lock_write(token)
 
353
 
 
354
    def unlock(self):
 
355
        self._ensure_real()
 
356
        return self._real_store.unlock()
 
357
 
 
358
    @needs_write_lock
 
359
    def save(self):
 
360
        # We need to be able to override the undecorated implementation
 
361
        self.save_without_locking()
 
362
 
 
363
    def save_without_locking(self):
 
364
        super(RemoteControlStore, self).save()
 
365
 
 
366
    def _ensure_real(self):
 
367
        self.bzrdir._ensure_real()
 
368
        if self._real_store is None:
 
369
            self._real_store = config.ControlStore(self.bzrdir)
 
370
 
 
371
    def external_url(self):
 
372
        return self.bzrdir.user_url
 
373
 
 
374
    def _load_content(self):
 
375
        medium = self.bzrdir._client._medium
 
376
        path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
 
377
        try:
 
378
            response, handler = self.bzrdir._call_expecting_body(
 
379
                'BzrDir.get_config_file', path)
 
380
        except errors.UnknownSmartMethod:
 
381
            self._ensure_real()
 
382
            return self._real_store._load_content()
 
383
        if len(response) and response[0] != 'ok':
 
384
            raise errors.UnexpectedSmartServerResponse(response)
 
385
        return handler.read_body_bytes()
 
386
 
 
387
    def _save_content(self, content):
 
388
        # FIXME JRV 2011-11-22: Ideally this should use a
 
389
        # HPSS call too, but at the moment it is not possible
 
390
        # to write lock control directories.
 
391
        self._ensure_real()
 
392
        return self._real_store._save_content(content)
 
393
 
 
394
 
 
395
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
396
    """Control directory on a remote server, accessed via bzr:// or similar."""
93
397
 
94
398
    def __init__(self, transport, format, _client=None, _force_probe=False):
97
401
        :param _client: Private parameter for testing. Disables probing and the
98
402
            use of a real bzrdir.
99
403
        """
100
 
        BzrDir.__init__(self, transport, format)
 
404
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
101
405
        # this object holds a delegated bzrdir that uses file-level operations
102
406
        # to talk to the other side
103
407
        self._real_bzrdir = None
163
467
                import traceback
164
468
                warning('VFS BzrDir access triggered\n%s',
165
469
                    ''.join(traceback.format_stack()))
166
 
            self._real_bzrdir = BzrDir.open_from_transport(
 
470
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
167
471
                self.root_transport, _server_formats=False)
168
472
            self._format._network_name = \
169
473
                self._real_bzrdir._format.network_name()
175
479
        # Prevent aliasing problems in the next_open_branch_result cache.
176
480
        # See create_branch for rationale.
177
481
        self._next_open_branch_result = None
178
 
        return BzrDir.break_lock(self)
 
482
        return _mod_bzrdir.BzrDir.break_lock(self)
179
483
 
180
484
    def _vfs_cloning_metadir(self, require_stacking=False):
181
485
        self._ensure_real()
212
516
        if len(branch_info) != 2:
213
517
            raise errors.UnexpectedSmartServerResponse(response)
214
518
        branch_ref, branch_name = branch_info
215
 
        format = bzrdir.network_format_registry.get(control_name)
 
519
        format = controldir.network_format_registry.get(control_name)
216
520
        if repo_name:
217
 
            format.repository_format = repository.network_format_registry.get(
 
521
            format.repository_format = _mod_repository.network_format_registry.get(
218
522
                repo_name)
219
523
        if branch_ref == 'ref':
220
524
            # XXX: we need possible_transports here to avoid reopening the
221
525
            # connection to the referenced location
222
 
            ref_bzrdir = BzrDir.open(branch_name)
 
526
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
527
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
528
            format.set_branch_format(branch_format)
225
529
        elif branch_ref == 'branch':
241
545
 
242
546
    def destroy_repository(self):
243
547
        """See BzrDir.destroy_repository"""
244
 
        self._ensure_real()
245
 
        self._real_bzrdir.destroy_repository()
 
548
        path = self._path_for_remote_call(self._client)
 
549
        try:
 
550
            response = self._call('BzrDir.destroy_repository', path)
 
551
        except errors.UnknownSmartMethod:
 
552
            self._ensure_real()
 
553
            self._real_bzrdir.destroy_repository()
 
554
            return
 
555
        if response[0] != 'ok':
 
556
            raise SmartProtocolError('unexpected response code %s' % (response,))
246
557
 
247
 
    def create_branch(self, name=None):
 
558
    def create_branch(self, name=None, repository=None,
 
559
                      append_revisions_only=None):
248
560
        # as per meta1 formats - just delegate to the format object which may
249
561
        # be parameterised.
250
562
        real_branch = self._format.get_branch_format().initialize(self,
251
 
            name=name)
 
563
            name=name, repository=repository,
 
564
            append_revisions_only=append_revisions_only)
252
565
        if not isinstance(real_branch, RemoteBranch):
253
 
            result = RemoteBranch(self, self.find_repository(), real_branch,
254
 
                                  name=name)
 
566
            if not isinstance(repository, RemoteRepository):
 
567
                raise AssertionError(
 
568
                    'need a RemoteRepository to use with RemoteBranch, got %r'
 
569
                    % (repository,))
 
570
            result = RemoteBranch(self, repository, real_branch, name=name)
255
571
        else:
256
572
            result = real_branch
257
573
        # BzrDir.clone_on_transport() uses the result of create_branch but does
265
581
 
266
582
    def destroy_branch(self, name=None):
267
583
        """See BzrDir.destroy_branch"""
268
 
        self._ensure_real()
269
 
        self._real_bzrdir.destroy_branch(name=name)
 
584
        path = self._path_for_remote_call(self._client)
 
585
        try:
 
586
            if name is not None:
 
587
                args = (name, )
 
588
            else:
 
589
                args = ()
 
590
            response = self._call('BzrDir.destroy_branch', path, *args)
 
591
        except errors.UnknownSmartMethod:
 
592
            self._ensure_real()
 
593
            self._real_bzrdir.destroy_branch(name=name)
 
594
            self._next_open_branch_result = None
 
595
            return
270
596
        self._next_open_branch_result = None
 
597
        if response[0] != 'ok':
 
598
            raise SmartProtocolError('unexpected response code %s' % (response,))
271
599
 
272
 
    def create_workingtree(self, revision_id=None, from_branch=None):
 
600
    def create_workingtree(self, revision_id=None, from_branch=None,
 
601
        accelerator_tree=None, hardlink=False):
273
602
        raise errors.NotLocalUrl(self.transport.base)
274
603
 
275
 
    def find_branch_format(self):
 
604
    def find_branch_format(self, name=None):
276
605
        """Find the branch 'format' for this bzrdir.
277
606
 
278
607
        This might be a synthetic object for e.g. RemoteBranch and SVN.
279
608
        """
280
 
        b = self.open_branch()
 
609
        b = self.open_branch(name=name)
281
610
        return b._format
282
611
 
283
 
    def get_branch_reference(self):
 
612
    def get_branch_reference(self, name=None):
284
613
        """See BzrDir.get_branch_reference()."""
 
614
        if name is not None:
 
615
            # XXX JRV20100304: Support opening colocated branches
 
616
            raise errors.NoColocatedBranchSupport(self)
285
617
        response = self._get_branch_reference()
286
618
        if response[0] == 'ref':
287
619
            return response[1]
318
650
            raise errors.UnexpectedSmartServerResponse(response)
319
651
        return response
320
652
 
321
 
    def _get_tree_branch(self):
 
653
    def _get_tree_branch(self, name=None):
322
654
        """See BzrDir._get_tree_branch()."""
323
 
        return None, self.open_branch()
 
655
        return None, self.open_branch(name=name)
324
656
 
325
657
    def open_branch(self, name=None, unsupported=False,
326
658
                    ignore_fallbacks=False):
411
743
 
412
744
    def has_workingtree(self):
413
745
        if self._has_working_tree is None:
414
 
            self._ensure_real()
415
 
            self._has_working_tree = self._real_bzrdir.has_workingtree()
 
746
            path = self._path_for_remote_call(self._client)
 
747
            try:
 
748
                response = self._call('BzrDir.has_workingtree', path)
 
749
            except errors.UnknownSmartMethod:
 
750
                self._ensure_real()
 
751
                self._has_working_tree = self._real_bzrdir.has_workingtree()
 
752
            else:
 
753
                if response[0] not in ('yes', 'no'):
 
754
                    raise SmartProtocolError('unexpected response code %s' % (response,))
 
755
                self._has_working_tree = (response[0] == 'yes')
416
756
        return self._has_working_tree
417
757
 
418
758
    def open_workingtree(self, recommend_upgrade=True):
423
763
 
424
764
    def _path_for_remote_call(self, client):
425
765
        """Return the path to be used for this bzrdir in a remote call."""
426
 
        return client.remote_path_from_transport(self.root_transport)
 
766
        return urlutils.split_segment_parameters_raw(
 
767
            client.remote_path_from_transport(self.root_transport))[0]
427
768
 
428
769
    def get_branch_transport(self, branch_format, name=None):
429
770
        self._ensure_real()
441
782
        """Upgrading of remote bzrdirs is not supported yet."""
442
783
        return False
443
784
 
444
 
    def needs_format_conversion(self, format=None):
 
785
    def needs_format_conversion(self, format):
445
786
        """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
787
        return False
450
788
 
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
789
    def _get_config(self):
458
790
        return RemoteBzrDirConfig(self)
459
791
 
460
 
 
461
 
class RemoteRepositoryFormat(repository.RepositoryFormat):
 
792
    def _get_config_store(self):
 
793
        return RemoteControlStore(self)
 
794
 
 
795
 
 
796
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
797
    """Format for repositories accessed over a _SmartClient.
463
798
 
464
799
    Instances of this repository are represented by RemoteRepository
479
814
    """
480
815
 
481
816
    _matchingbzrdir = RemoteBzrDirFormat()
 
817
    supports_full_versioned_files = True
 
818
    supports_leaving_lock = True
482
819
 
483
820
    def __init__(self):
484
 
        repository.RepositoryFormat.__init__(self)
 
821
        _mod_repository.RepositoryFormat.__init__(self)
485
822
        self._custom_format = None
486
823
        self._network_name = None
487
824
        self._creating_bzrdir = None
 
825
        self._revision_graph_can_have_wrong_parents = None
488
826
        self._supports_chks = None
489
827
        self._supports_external_lookups = None
490
828
        self._supports_tree_reference = None
 
829
        self._supports_funky_characters = None
 
830
        self._supports_nesting_repositories = None
491
831
        self._rich_root_data = None
492
832
 
493
833
    def __repr__(self):
522
862
        return self._supports_external_lookups
523
863
 
524
864
    @property
 
865
    def supports_funky_characters(self):
 
866
        if self._supports_funky_characters is None:
 
867
            self._ensure_real()
 
868
            self._supports_funky_characters = \
 
869
                self._custom_format.supports_funky_characters
 
870
        return self._supports_funky_characters
 
871
 
 
872
    @property
 
873
    def supports_nesting_repositories(self):
 
874
        if self._supports_nesting_repositories is None:
 
875
            self._ensure_real()
 
876
            self._supports_nesting_repositories = \
 
877
                self._custom_format.supports_nesting_repositories
 
878
        return self._supports_nesting_repositories
 
879
 
 
880
    @property
525
881
    def supports_tree_reference(self):
526
882
        if self._supports_tree_reference is None:
527
883
            self._ensure_real()
529
885
                self._custom_format.supports_tree_reference
530
886
        return self._supports_tree_reference
531
887
 
 
888
    @property
 
889
    def revision_graph_can_have_wrong_parents(self):
 
890
        if self._revision_graph_can_have_wrong_parents is None:
 
891
            self._ensure_real()
 
892
            self._revision_graph_can_have_wrong_parents = \
 
893
                self._custom_format.revision_graph_can_have_wrong_parents
 
894
        return self._revision_graph_can_have_wrong_parents
 
895
 
532
896
    def _vfs_initialize(self, a_bzrdir, shared):
533
897
        """Helper for common code in initialize."""
534
898
        if self._custom_format:
569
933
            network_name = self._network_name
570
934
        else:
571
935
            # Select the current bzrlib default and ask for that.
572
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
936
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
573
937
            reference_format = reference_bzrdir_format.repository_format
574
938
            network_name = reference_format.network_name()
575
939
        # 2) try direct creation via RPC
601
965
 
602
966
    def _ensure_real(self):
603
967
        if self._custom_format is None:
604
 
            self._custom_format = repository.network_format_registry.get(
 
968
            self._custom_format = _mod_repository.network_format_registry.get(
605
969
                self._network_name)
606
970
 
607
971
    @property
643
1007
        return self._custom_format._serializer
644
1008
 
645
1009
 
646
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
647
 
    bzrdir.ControlComponent):
 
1010
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
 
1011
        lock._RelockDebugMixin):
648
1012
    """Repository accessed over rpc.
649
1013
 
650
1014
    For the moment most operations are performed using local transport-backed
674
1038
        self._format = format
675
1039
        self._lock_mode = None
676
1040
        self._lock_token = None
 
1041
        self._write_group_tokens = None
677
1042
        self._lock_count = 0
678
1043
        self._leave_lock = False
679
1044
        # Cache of revision parents; misses are cached during read locks, and
703
1068
        # transport, but I'm not sure it's worth making this method
704
1069
        # optional -- mbp 2010-04-21
705
1070
        return self.bzrdir.get_repository_transport(None)
706
 
        
 
1071
 
707
1072
    def __str__(self):
708
1073
        return "%s(%s)" % (self.__class__.__name__, self.base)
709
1074
 
719
1084
 
720
1085
        :param suppress_errors: see Repository.abort_write_group.
721
1086
        """
722
 
        self._ensure_real()
723
 
        return self._real_repository.abort_write_group(
724
 
            suppress_errors=suppress_errors)
 
1087
        if self._real_repository:
 
1088
            self._ensure_real()
 
1089
            return self._real_repository.abort_write_group(
 
1090
                suppress_errors=suppress_errors)
 
1091
        if not self.is_in_write_group():
 
1092
            if suppress_errors:
 
1093
                mutter('(suppressed) not in write group')
 
1094
                return
 
1095
            raise errors.BzrError("not in write group")
 
1096
        path = self.bzrdir._path_for_remote_call(self._client)
 
1097
        try:
 
1098
            response = self._call('Repository.abort_write_group', path,
 
1099
                self._lock_token, self._write_group_tokens)
 
1100
        except Exception, exc:
 
1101
            self._write_group = None
 
1102
            if not suppress_errors:
 
1103
                raise
 
1104
            mutter('abort_write_group failed')
 
1105
            log_exception_quietly()
 
1106
            note(gettext('bzr: ERROR (ignored): %s'), exc)
 
1107
        else:
 
1108
            if response != ('ok', ):
 
1109
                raise errors.UnexpectedSmartServerResponse(response)
 
1110
            self._write_group_tokens = None
725
1111
 
726
1112
    @property
727
1113
    def chk_bytes(self):
741
1127
        for older plugins that don't use e.g. the CommitBuilder
742
1128
        facility.
743
1129
        """
744
 
        self._ensure_real()
745
 
        return self._real_repository.commit_write_group()
 
1130
        if self._real_repository:
 
1131
            self._ensure_real()
 
1132
            return self._real_repository.commit_write_group()
 
1133
        if not self.is_in_write_group():
 
1134
            raise errors.BzrError("not in write group")
 
1135
        path = self.bzrdir._path_for_remote_call(self._client)
 
1136
        response = self._call('Repository.commit_write_group', path,
 
1137
            self._lock_token, self._write_group_tokens)
 
1138
        if response != ('ok', ):
 
1139
            raise errors.UnexpectedSmartServerResponse(response)
 
1140
        self._write_group_tokens = None
746
1141
 
747
1142
    def resume_write_group(self, tokens):
748
 
        self._ensure_real()
749
 
        return self._real_repository.resume_write_group(tokens)
 
1143
        if self._real_repository:
 
1144
            return self._real_repository.resume_write_group(tokens)
 
1145
        path = self.bzrdir._path_for_remote_call(self._client)
 
1146
        try:
 
1147
            response = self._call('Repository.check_write_group', path,
 
1148
               self._lock_token, tokens)
 
1149
        except errors.UnknownSmartMethod:
 
1150
            self._ensure_real()
 
1151
            return self._real_repository.resume_write_group(tokens)
 
1152
        if response != ('ok', ):
 
1153
            raise errors.UnexpectedSmartServerResponse(response)
 
1154
        self._write_group_tokens = tokens
750
1155
 
751
1156
    def suspend_write_group(self):
752
 
        self._ensure_real()
753
 
        return self._real_repository.suspend_write_group()
 
1157
        if self._real_repository:
 
1158
            return self._real_repository.suspend_write_group()
 
1159
        ret = self._write_group_tokens or []
 
1160
        self._write_group_tokens = None
 
1161
        return ret
754
1162
 
755
1163
    def get_missing_parent_inventories(self, check_for_missing_texts=True):
756
1164
        self._ensure_real()
817
1225
    def find_text_key_references(self):
818
1226
        """Find the text key references within the repository.
819
1227
 
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
1228
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
824
1229
            to whether they were referred to by the inventory of the
825
1230
            revision_id that they contain. The inventory texts from all present
843
1248
        """Private method for using with old (< 1.2) servers to fallback."""
844
1249
        if revision_id is None:
845
1250
            revision_id = ''
846
 
        elif revision.is_null(revision_id):
 
1251
        elif _mod_revision.is_null(revision_id):
847
1252
            return {}
848
1253
 
849
1254
        path = self.bzrdir._path_for_remote_call(self._client)
873
1278
        return RemoteStreamSource(self, to_format)
874
1279
 
875
1280
    @needs_read_lock
 
1281
    def get_file_graph(self):
 
1282
        return graph.Graph(self.texts)
 
1283
 
 
1284
    @needs_read_lock
876
1285
    def has_revision(self, revision_id):
877
1286
        """True if this repository has a copy of the revision."""
878
1287
        # Copy of bzrlib.repository.Repository.has_revision
895
1304
    def _has_same_fallbacks(self, other_repo):
896
1305
        """Returns true if the repositories have the same fallbacks."""
897
1306
        # XXX: copied from Repository; it should be unified into a base class
898
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
 
1307
        # <https://bugs.launchpad.net/bzr/+bug/401622>
899
1308
        my_fb = self._fallback_repositories
900
1309
        other_fb = other_repo._fallback_repositories
901
1310
        if len(my_fb) != len(other_fb):
930
1339
        """See Repository.gather_stats()."""
931
1340
        path = self.bzrdir._path_for_remote_call(self._client)
932
1341
        # revid can be None to indicate no revisions, not just NULL_REVISION
933
 
        if revid is None or revision.is_null(revid):
 
1342
        if revid is None or _mod_revision.is_null(revid):
934
1343
            fmt_revid = ''
935
1344
        else:
936
1345
            fmt_revid = revid
965
1374
 
966
1375
    def get_physical_lock_status(self):
967
1376
        """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()
 
1377
        path = self.bzrdir._path_for_remote_call(self._client)
 
1378
        try:
 
1379
            response = self._call('Repository.get_physical_lock_status', path)
 
1380
        except errors.UnknownSmartMethod:
 
1381
            self._ensure_real()
 
1382
            return self._real_repository.get_physical_lock_status()
 
1383
        if response[0] not in ('yes', 'no'):
 
1384
            raise errors.UnexpectedSmartServerResponse(response)
 
1385
        return (response[0] == 'yes')
971
1386
 
972
1387
    def is_in_write_group(self):
973
1388
        """Return True if there is an open write group.
974
1389
 
975
1390
        write groups are only applicable locally for the smart server..
976
1391
        """
 
1392
        if self._write_group_tokens is not None:
 
1393
            return True
977
1394
        if self._real_repository:
978
1395
            return self._real_repository.is_in_write_group()
979
1396
 
997
1414
        pass
998
1415
 
999
1416
    def lock_read(self):
 
1417
        """Lock the repository for read operations.
 
1418
 
 
1419
        :return: A bzrlib.lock.LogicalLockResult.
 
1420
        """
1000
1421
        # wrong eventually - want a local lock cache context
1001
1422
        if not self._lock_mode:
1002
1423
            self._note_lock('r')
1009
1430
                repo.lock_read()
1010
1431
        else:
1011
1432
            self._lock_count += 1
 
1433
        return lock.LogicalLockResult(self.unlock)
1012
1434
 
1013
1435
    def _remote_lock_write(self, token):
1014
1436
        path = self.bzrdir._path_for_remote_call(self._client)
1054
1476
            raise errors.ReadOnlyError(self)
1055
1477
        else:
1056
1478
            self._lock_count += 1
1057
 
        return self._lock_token or None
 
1479
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
1058
1480
 
1059
1481
    def leave_lock_in_place(self):
1060
1482
        if not self._lock_token:
1109
1531
            self._real_repository.lock_write(self._lock_token)
1110
1532
        elif self._lock_mode == 'r':
1111
1533
            self._real_repository.lock_read()
 
1534
        if self._write_group_tokens is not None:
 
1535
            # if we are already in a write group, resume it
 
1536
            self._real_repository.resume_write_group(self._write_group_tokens)
 
1537
            self._write_group_tokens = None
1112
1538
 
1113
1539
    def start_write_group(self):
1114
1540
        """Start a write group on the decorated repository.
1118
1544
        for older plugins that don't use e.g. the CommitBuilder
1119
1545
        facility.
1120
1546
        """
1121
 
        self._ensure_real()
1122
 
        return self._real_repository.start_write_group()
 
1547
        if self._real_repository:
 
1548
            self._ensure_real()
 
1549
            return self._real_repository.start_write_group()
 
1550
        if not self.is_write_locked():
 
1551
            raise errors.NotWriteLocked(self)
 
1552
        if self._write_group_tokens is not None:
 
1553
            raise errors.BzrError('already in a write group')
 
1554
        path = self.bzrdir._path_for_remote_call(self._client)
 
1555
        try:
 
1556
            response = self._call('Repository.start_write_group', path,
 
1557
                self._lock_token)
 
1558
        except (errors.UnknownSmartMethod, errors.UnsuspendableWriteGroup):
 
1559
            self._ensure_real()
 
1560
            return self._real_repository.start_write_group()
 
1561
        if response[0] != 'ok':
 
1562
            raise errors.UnexpectedSmartServerResponse(response)
 
1563
        self._write_group_tokens = response[1]
1123
1564
 
1124
1565
    def _unlock(self, token):
1125
1566
        path = self.bzrdir._path_for_remote_call(self._client)
1152
1593
            # This is just to let the _real_repository stay up to date.
1153
1594
            if self._real_repository is not None:
1154
1595
                self._real_repository.unlock()
 
1596
            elif self._write_group_tokens is not None:
 
1597
                self.abort_write_group()
1155
1598
        finally:
1156
1599
            # The rpc-level lock should be released even if there was a
1157
1600
            # problem releasing the vfs-based lock.
1169
1612
 
1170
1613
    def break_lock(self):
1171
1614
        # should hand off to the network
1172
 
        self._ensure_real()
1173
 
        return self._real_repository.break_lock()
 
1615
        path = self.bzrdir._path_for_remote_call(self._client)
 
1616
        try:
 
1617
            response = self._call("Repository.break_lock", path)
 
1618
        except errors.UnknownSmartMethod:
 
1619
            self._ensure_real()
 
1620
            return self._real_repository.break_lock()
 
1621
        if response != ('ok',):
 
1622
            raise errors.UnexpectedSmartServerResponse(response)
1174
1623
 
1175
1624
    def _get_tarball(self, compression):
1176
1625
        """Return a TemporaryFile containing a repository tarball.
1194
1643
            return t
1195
1644
        raise errors.UnexpectedSmartServerResponse(response)
1196
1645
 
 
1646
    @needs_read_lock
1197
1647
    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)
 
1648
        """Create a descendent repository for new development.
 
1649
 
 
1650
        Unlike clone, this does not copy the settings of the repository.
 
1651
        """
 
1652
        dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1202
1653
        dest_repo.fetch(self, revision_id=revision_id)
1203
1654
        return dest_repo
1204
1655
 
 
1656
    def _create_sprouting_repo(self, a_bzrdir, shared):
 
1657
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
 
1658
            # use target default format.
 
1659
            dest_repo = a_bzrdir.create_repository()
 
1660
        else:
 
1661
            # Most control formats need the repository to be specifically
 
1662
            # created, but on some old all-in-one formats it's not needed
 
1663
            try:
 
1664
                dest_repo = self._format.initialize(a_bzrdir, shared=shared)
 
1665
            except errors.UninitializableFormat:
 
1666
                dest_repo = a_bzrdir.open_repository()
 
1667
        return dest_repo
 
1668
 
1205
1669
    ### These methods are just thin shims to the VFS object for now.
1206
1670
 
 
1671
    @needs_read_lock
1207
1672
    def revision_tree(self, revision_id):
1208
 
        self._ensure_real()
1209
 
        return self._real_repository.revision_tree(revision_id)
 
1673
        revision_id = _mod_revision.ensure_null(revision_id)
 
1674
        if revision_id == _mod_revision.NULL_REVISION:
 
1675
            return InventoryRevisionTree(self,
 
1676
                Inventory(root_id=None), _mod_revision.NULL_REVISION)
 
1677
        else:
 
1678
            return list(self.revision_trees([revision_id]))[0]
1210
1679
 
1211
1680
    def get_serializer_format(self):
1212
 
        self._ensure_real()
1213
 
        return self._real_repository.get_serializer_format()
 
1681
        path = self.bzrdir._path_for_remote_call(self._client)
 
1682
        try:
 
1683
            response = self._call('VersionedFileRepository.get_serializer_format',
 
1684
                path)
 
1685
        except errors.UnknownSmartMethod:
 
1686
            self._ensure_real()
 
1687
            return self._real_repository.get_serializer_format()
 
1688
        if response[0] != 'ok':
 
1689
            raise errors.UnexpectedSmartServerResponse(response)
 
1690
        return response[1]
1214
1691
 
1215
1692
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1693
                           timezone=None, committer=None, revprops=None,
1217
 
                           revision_id=None):
 
1694
                           revision_id=None, lossy=False):
1218
1695
        # FIXME: It ought to be possible to call this without immediately
1219
1696
        # triggering _ensure_real.  For now it's the easiest thing to do.
1220
1697
        self._ensure_real()
1221
1698
        real_repo = self._real_repository
1222
1699
        builder = real_repo.get_commit_builder(branch, parents,
1223
1700
                config, timestamp=timestamp, timezone=timezone,
1224
 
                committer=committer, revprops=revprops, revision_id=revision_id)
 
1701
                committer=committer, revprops=revprops,
 
1702
                revision_id=revision_id, lossy=lossy)
1225
1703
        return builder
1226
1704
 
1227
1705
    def add_fallback_repository(self, repository):
1235
1713
        # We need to accumulate additional repositories here, to pass them in
1236
1714
        # on various RPC's.
1237
1715
        #
 
1716
        # Make the check before we lock: this raises an exception.
 
1717
        self._check_fallback_repository(repository)
1238
1718
        if self.is_locked():
1239
1719
            # We will call fallback.unlock() when we transition to the unlocked
1240
1720
            # state, so always add a lock here. If a caller passes us a locked
1241
1721
            # repository, they are responsible for unlocking it later.
1242
1722
            repository.lock_read()
1243
 
        self._check_fallback_repository(repository)
1244
1723
        self._fallback_repositories.append(repository)
1245
1724
        # If self._real_repository was parameterised already (e.g. because a
1246
1725
        # _real_branch had its get_stacked_on_url method called), then the
1288
1767
 
1289
1768
    @needs_read_lock
1290
1769
    def get_revision(self, revision_id):
1291
 
        self._ensure_real()
1292
 
        return self._real_repository.get_revision(revision_id)
 
1770
        return self.get_revisions([revision_id])[0]
1293
1771
 
1294
1772
    def get_transaction(self):
1295
1773
        self._ensure_real()
1297
1775
 
1298
1776
    @needs_read_lock
1299
1777
    def clone(self, a_bzrdir, revision_id=None):
1300
 
        self._ensure_real()
1301
 
        return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
 
1778
        dest_repo = self._create_sprouting_repo(
 
1779
            a_bzrdir, shared=self.is_shared())
 
1780
        self.copy_content_into(dest_repo, revision_id)
 
1781
        return dest_repo
1302
1782
 
1303
1783
    def make_working_trees(self):
1304
1784
        """See Repository.make_working_trees"""
1305
 
        self._ensure_real()
1306
 
        return self._real_repository.make_working_trees()
 
1785
        path = self.bzrdir._path_for_remote_call(self._client)
 
1786
        try:
 
1787
            response = self._call('Repository.make_working_trees', path)
 
1788
        except errors.UnknownSmartMethod:
 
1789
            self._ensure_real()
 
1790
            return self._real_repository.make_working_trees()
 
1791
        if response[0] not in ('yes', 'no'):
 
1792
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
1793
        return response[0] == 'yes'
1307
1794
 
1308
1795
    def refresh_data(self):
1309
 
        """Re-read any data needed to to synchronise with disk.
 
1796
        """Re-read any data needed to synchronise with disk.
1310
1797
 
1311
1798
        This method is intended to be called after another repository instance
1312
1799
        (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.
 
1800
        repository. On all repositories this will work outside of write groups.
 
1801
        Some repository formats (pack and newer for bzrlib native formats)
 
1802
        support refresh_data inside write groups. If called inside a write
 
1803
        group on a repository that does not support refreshing in a write group
 
1804
        IsInWriteGroupError will be raised.
1315
1805
        """
1316
 
        if self.is_in_write_group():
1317
 
            raise errors.InternalBzrError(
1318
 
                "May not refresh_data while in a write group.")
1319
1806
        if self._real_repository is not None:
1320
1807
            self._real_repository.refresh_data()
1321
1808
 
1333
1820
        return result
1334
1821
 
1335
1822
    @needs_read_lock
1336
 
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
1823
    def search_missing_revision_ids(self, other,
 
1824
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
1825
            find_ghosts=True, revision_ids=None, if_present_ids=None,
 
1826
            limit=None):
1337
1827
        """Return the revision ids that other has that this does not.
1338
1828
 
1339
1829
        These are returned in topological order.
1340
1830
 
1341
1831
        revision_id: only return revision ids included by revision_id.
1342
1832
        """
1343
 
        return repository.InterRepository.get(
1344
 
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
 
1833
        if symbol_versioning.deprecated_passed(revision_id):
 
1834
            symbol_versioning.warn(
 
1835
                'search_missing_revision_ids(revision_id=...) was '
 
1836
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
1837
                DeprecationWarning, stacklevel=2)
 
1838
            if revision_ids is not None:
 
1839
                raise AssertionError(
 
1840
                    'revision_ids is mutually exclusive with revision_id')
 
1841
            if revision_id is not None:
 
1842
                revision_ids = [revision_id]
 
1843
        inter_repo = _mod_repository.InterRepository.get(other, self)
 
1844
        return inter_repo.search_missing_revision_ids(
 
1845
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
1846
            if_present_ids=if_present_ids, limit=limit)
1345
1847
 
1346
 
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
 
1848
    def fetch(self, source, revision_id=None, find_ghosts=False,
1347
1849
            fetch_spec=None):
1348
1850
        # No base implementation to use as RemoteRepository is not a subclass
1349
1851
        # of Repository; so this is a copy of Repository.fetch().
1360
1862
            # check that last_revision is in 'from' and then return a
1361
1863
            # no-operation.
1362
1864
            if (revision_id is not None and
1363
 
                not revision.is_null(revision_id)):
 
1865
                not _mod_revision.is_null(revision_id)):
1364
1866
                self.get_revision(revision_id)
1365
1867
            return 0, []
1366
1868
        # if there is no specific appropriate InterRepository, this will get
1367
1869
        # the InterRepository base class, which raises an
1368
1870
        # IncompatibleRepositories when asked to fetch.
1369
 
        inter = repository.InterRepository.get(source, self)
1370
 
        return inter.fetch(revision_id=revision_id, pb=pb,
 
1871
        inter = _mod_repository.InterRepository.get(source, self)
 
1872
        return inter.fetch(revision_id=revision_id,
1371
1873
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1372
1874
 
1373
1875
    def create_bundle(self, target, base, fileobj, format=None):
1375
1877
        self._real_repository.create_bundle(target, base, fileobj, format)
1376
1878
 
1377
1879
    @needs_read_lock
 
1880
    @symbol_versioning.deprecated_method(
 
1881
        symbol_versioning.deprecated_in((2, 4, 0)))
1378
1882
    def get_ancestry(self, revision_id, topo_sorted=True):
1379
1883
        self._ensure_real()
1380
1884
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
1394
1898
        self._ensure_real()
1395
1899
        return self._real_repository.iter_files_bytes(desired_files)
1396
1900
 
 
1901
    def get_cached_parent_map(self, revision_ids):
 
1902
        """See bzrlib.CachingParentsProvider.get_cached_parent_map"""
 
1903
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
 
1904
 
1397
1905
    def get_parent_map(self, revision_ids):
1398
1906
        """See bzrlib.Graph.get_parent_map()."""
1399
1907
        return self._make_parents_provider().get_parent_map(revision_ids)
1457
1965
        if parents_map is None:
1458
1966
            # Repository is not locked, so there's no cache.
1459
1967
            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)
 
1968
        if _DEFAULT_SEARCH_DEPTH <= 0:
 
1969
            (start_set, stop_keys,
 
1970
             key_count) = graph.search_result_from_parent_map(
 
1971
                parents_map, self._unstacked_provider.missing_keys)
 
1972
        else:
 
1973
            (start_set, stop_keys,
 
1974
             key_count) = graph.limited_search_result_from_parent_map(
 
1975
                parents_map, self._unstacked_provider.missing_keys,
 
1976
                keys, depth=_DEFAULT_SEARCH_DEPTH)
1480
1977
        recipe = ('manual', start_set, stop_keys, key_count)
1481
1978
        body = self._serialise_search_recipe(recipe)
1482
1979
        path = self.bzrdir._path_for_remote_call(self._client)
1544
2041
        return self._real_repository.reconcile(other=other, thorough=thorough)
1545
2042
 
1546
2043
    def all_revision_ids(self):
1547
 
        self._ensure_real()
1548
 
        return self._real_repository.all_revision_ids()
 
2044
        path = self.bzrdir._path_for_remote_call(self._client)
 
2045
        try:
 
2046
            response_tuple, response_handler = self._call_expecting_body(
 
2047
                "Repository.all_revision_ids", path)
 
2048
        except errors.UnknownSmartMethod:
 
2049
            self._ensure_real()
 
2050
            return self._real_repository.all_revision_ids()
 
2051
        if response_tuple != ("ok", ):
 
2052
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2053
        revids = set(response_handler.read_body_bytes().splitlines())
 
2054
        for fallback in self._fallback_repositories:
 
2055
            revids.update(set(fallback.all_revision_ids()))
 
2056
        return list(revids)
1549
2057
 
1550
2058
    @needs_read_lock
1551
2059
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1555
2063
 
1556
2064
    @needs_read_lock
1557
2065
    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)
 
2066
        r = self.get_revision(revision_id)
 
2067
        return list(self.get_deltas_for_revisions([r],
 
2068
            specific_fileids=specific_fileids))[0]
1561
2069
 
1562
2070
    @needs_read_lock
1563
2071
    def revision_trees(self, revision_ids):
1576
2084
            callback_refs=callback_refs, check_repo=check_repo)
1577
2085
 
1578
2086
    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)
 
2087
        """Make a complete copy of the content in self into destination.
 
2088
 
 
2089
        This is a destructive operation! Do not use it on existing
 
2090
        repositories.
 
2091
        """
 
2092
        interrepo = _mod_repository.InterRepository.get(self, destination)
 
2093
        return interrepo.copy_content(revision_id)
1582
2094
 
1583
2095
    def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2096
        # get a tarball of the remote repository, and copy from that into the
1586
2098
        from bzrlib import osutils
1587
2099
        import tarfile
1588
2100
        # TODO: Maybe a progress bar while streaming the tarball?
1589
 
        note("Copying repository content as tarball...")
 
2101
        note(gettext("Copying repository content as tarball..."))
1590
2102
        tar_file = self._get_tarball('bz2')
1591
2103
        if tar_file is None:
1592
2104
            return None
1597
2109
            tmpdir = osutils.mkdtemp()
1598
2110
            try:
1599
2111
                _extract_tar(tar, tmpdir)
1600
 
                tmp_bzrdir = BzrDir.open(tmpdir)
 
2112
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1601
2113
                tmp_repo = tmp_bzrdir.open_repository()
1602
2114
                tmp_repo.copy_content_into(destination, revision_id)
1603
2115
            finally:
1621
2133
    @needs_write_lock
1622
2134
    def pack(self, hint=None, clean_obsolete_packs=False):
1623
2135
        """Compress the data within the repository.
1624
 
 
1625
 
        This is not currently implemented within the smart server.
1626
2136
        """
1627
 
        self._ensure_real()
1628
 
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
 
2137
        if hint is None:
 
2138
            body = ""
 
2139
        else:
 
2140
            body = "".join([l+"\n" for l in hint])
 
2141
        path = self.bzrdir._path_for_remote_call(self._client)
 
2142
        try:
 
2143
            response, handler = self._call_with_body_bytes_expecting_body(
 
2144
                'Repository.pack', (path, self._lock_token,
 
2145
                    str(clean_obsolete_packs)), body)
 
2146
        except errors.UnknownSmartMethod:
 
2147
            self._ensure_real()
 
2148
            return self._real_repository.pack(hint=hint,
 
2149
                clean_obsolete_packs=clean_obsolete_packs)
 
2150
        handler.cancel_read_body()
 
2151
        if response != ('ok', ):
 
2152
            raise errors.UnexpectedSmartServerResponse(response)
1629
2153
 
1630
2154
    @property
1631
2155
    def revisions(self):
1667
2191
 
1668
2192
    @needs_write_lock
1669
2193
    def sign_revision(self, revision_id, gpg_strategy):
1670
 
        self._ensure_real()
1671
 
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
 
2194
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2195
        plaintext = testament.as_short_text()
 
2196
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1672
2197
 
1673
2198
    @property
1674
2199
    def texts(self):
1688
2213
    def supports_rich_root(self):
1689
2214
        return self._format.rich_root_data
1690
2215
 
 
2216
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1691
2217
    def iter_reverse_revision_history(self, revision_id):
1692
2218
        self._ensure_real()
1693
2219
        return self._real_repository.iter_reverse_revision_history(revision_id)
1696
2222
    def _serializer(self):
1697
2223
        return self._format._serializer
1698
2224
 
 
2225
    @needs_write_lock
1699
2226
    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)
 
2227
        signature = gpg_strategy.sign(plaintext)
 
2228
        self.add_signature_text(revision_id, signature)
1703
2229
 
1704
2230
    def add_signature_text(self, revision_id, signature):
1705
 
        self._ensure_real()
1706
 
        return self._real_repository.add_signature_text(revision_id, signature)
 
2231
        if self._real_repository:
 
2232
            # If there is a real repository the write group will
 
2233
            # be in the real repository as well, so use that:
 
2234
            self._ensure_real()
 
2235
            return self._real_repository.add_signature_text(
 
2236
                revision_id, signature)
 
2237
        path = self.bzrdir._path_for_remote_call(self._client)
 
2238
        response, response_handler = self._call_with_body_bytes(
 
2239
            'Repository.add_signature_text', (path, revision_id),
 
2240
            signature)
 
2241
        self.refresh_data()
 
2242
        if response[0] != 'ok':
 
2243
            raise errors.UnexpectedSmartServerResponse(response)
1707
2244
 
1708
2245
    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)
 
2246
        path = self.bzrdir._path_for_remote_call(self._client)
 
2247
        try:
 
2248
            response = self._call('Repository.has_signature_for_revision_id',
 
2249
                path, revision_id)
 
2250
        except errors.UnknownSmartMethod:
 
2251
            self._ensure_real()
 
2252
            return self._real_repository.has_signature_for_revision_id(
 
2253
                revision_id)
 
2254
        if response[0] not in ('yes', 'no'):
 
2255
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
2256
        return (response[0] == 'yes')
 
2257
 
 
2258
    @needs_read_lock
 
2259
    def verify_revision_signature(self, revision_id, gpg_strategy):
 
2260
        if not self.has_signature_for_revision_id(revision_id):
 
2261
            return gpg.SIGNATURE_NOT_SIGNED, None
 
2262
        signature = self.get_signature_text(revision_id)
 
2263
 
 
2264
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2265
        plaintext = testament.as_short_text()
 
2266
 
 
2267
        return gpg_strategy.verify(signature, plaintext)
1711
2268
 
1712
2269
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2270
        self._ensure_real()
1714
2271
        return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2272
            _files_pb=_files_pb)
1716
2273
 
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
2274
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2275
        self._ensure_real()
1724
2276
        return self._real_repository._find_inconsistent_revision_parents(
1732
2284
        providers = [self._unstacked_provider]
1733
2285
        if other is not None:
1734
2286
            providers.insert(0, other)
1735
 
        providers.extend(r._make_parents_provider() for r in
1736
 
                         self._fallback_repositories)
1737
 
        return graph.StackedParentsProvider(providers)
 
2287
        return graph.StackedParentsProvider(_LazyListJoin(
 
2288
            providers, self._fallback_repositories))
1738
2289
 
1739
2290
    def _serialise_search_recipe(self, recipe):
1740
2291
        """Serialise a graph search recipe.
1748
2299
        return '\n'.join((start_keys, stop_keys, count))
1749
2300
 
1750
2301
    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)]
 
2302
        parts = search_result.get_network_struct()
1757
2303
        return '\n'.join(parts)
1758
2304
 
1759
2305
    def autopack(self):
1769
2315
            raise errors.UnexpectedSmartServerResponse(response)
1770
2316
 
1771
2317
 
1772
 
class RemoteStreamSink(repository.StreamSink):
 
2318
class RemoteStreamSink(vf_repository.StreamSink):
1773
2319
 
1774
2320
    def _insert_real(self, stream, src_format, resume_tokens):
1775
2321
        self.target_repo._ensure_real()
1876
2422
        self._last_substream and self._last_stream so that the stream can be
1877
2423
        resumed by _resume_stream_with_vfs.
1878
2424
        """
1879
 
                    
 
2425
 
1880
2426
        stream_iter = iter(stream)
1881
2427
        for substream_kind, substream in stream_iter:
1882
2428
            if substream_kind == 'inventory-deltas':
1885
2431
                return
1886
2432
            else:
1887
2433
                yield substream_kind, substream
1888
 
            
1889
 
 
1890
 
class RemoteStreamSource(repository.StreamSource):
 
2434
 
 
2435
 
 
2436
class RemoteStreamSource(vf_repository.StreamSource):
1891
2437
    """Stream data from a remote server."""
1892
2438
 
1893
2439
    def get_stream(self, search):
1953
2499
        candidate_verbs = [
1954
2500
            ('Repository.get_stream_1.19', (1, 19)),
1955
2501
            ('Repository.get_stream', (1, 13))]
 
2502
 
1956
2503
        found_verb = False
1957
2504
        for verb, version in candidate_verbs:
1958
2505
            if medium._is_remote_before(version):
1962
2509
                    verb, args, search_bytes)
1963
2510
            except errors.UnknownSmartMethod:
1964
2511
                medium._remember_remote_is_before(version)
 
2512
            except errors.UnknownErrorFromSmartServer, e:
 
2513
                if isinstance(search, graph.EverythingResult):
 
2514
                    error_verb = e.error_from_smart_server.error_verb
 
2515
                    if error_verb == 'BadSearch':
 
2516
                        # Pre-2.4 servers don't support this sort of search.
 
2517
                        # XXX: perhaps falling back to VFS on BadSearch is a
 
2518
                        # good idea in general?  It might provide a little bit
 
2519
                        # of protection against client-side bugs.
 
2520
                        medium._remember_remote_is_before((2, 4))
 
2521
                        break
 
2522
                raise
1965
2523
            else:
1966
2524
                response_tuple, response_handler = response
1967
2525
                found_verb = True
1971
2529
        if response_tuple[0] != 'ok':
1972
2530
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1973
2531
        byte_stream = response_handler.read_streamed_body()
1974
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
 
2532
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
 
2533
            self._record_counter)
1975
2534
        if src_format.network_name() != repo._format.network_name():
1976
2535
            raise AssertionError(
1977
2536
                "Mismatched RemoteRepository and stream src %r, %r" % (
2063
2622
        return a_bzrdir.open_branch(name=name, 
2064
2623
            ignore_fallbacks=ignore_fallbacks)
2065
2624
 
2066
 
    def _vfs_initialize(self, a_bzrdir, name):
 
2625
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
2067
2626
        # Initialisation when using a local bzrdir object, or a non-vfs init
2068
2627
        # method is not available on the server.
2069
2628
        # self._custom_format is always set - the start of initialize ensures
2071
2630
        if isinstance(a_bzrdir, RemoteBzrDir):
2072
2631
            a_bzrdir._ensure_real()
2073
2632
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2074
 
                name)
 
2633
                name, append_revisions_only=append_revisions_only)
2075
2634
        else:
2076
2635
            # We assume the bzrdir is parameterised; it may not be.
2077
 
            result = self._custom_format.initialize(a_bzrdir, name)
 
2636
            result = self._custom_format.initialize(a_bzrdir, name,
 
2637
                append_revisions_only=append_revisions_only)
2078
2638
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2079
2639
            not isinstance(result, RemoteBranch)):
2080
2640
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2081
2641
                                  name=name)
2082
2642
        return result
2083
2643
 
2084
 
    def initialize(self, a_bzrdir, name=None):
 
2644
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2645
                   append_revisions_only=None):
2085
2646
        # 1) get the network name to use.
2086
2647
        if self._custom_format:
2087
2648
            network_name = self._custom_format.network_name()
2088
2649
        else:
2089
2650
            # Select the current bzrlib default and ask for that.
2090
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
2651
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
2091
2652
            reference_format = reference_bzrdir_format.get_branch_format()
2092
2653
            self._custom_format = reference_format
2093
2654
            network_name = reference_format.network_name()
2094
2655
        # Being asked to create on a non RemoteBzrDir:
2095
2656
        if not isinstance(a_bzrdir, RemoteBzrDir):
2096
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2657
            return self._vfs_initialize(a_bzrdir, name=name,
 
2658
                append_revisions_only=append_revisions_only)
2097
2659
        medium = a_bzrdir._client._medium
2098
2660
        if medium._is_remote_before((1, 13)):
2099
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2661
            return self._vfs_initialize(a_bzrdir, name=name,
 
2662
                append_revisions_only=append_revisions_only)
2100
2663
        # Creating on a remote bzr dir.
2101
2664
        # 2) try direct creation via RPC
2102
2665
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2109
2672
        except errors.UnknownSmartMethod:
2110
2673
            # Fallback - use vfs methods
2111
2674
            medium._remember_remote_is_before((1, 13))
2112
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
2675
            return self._vfs_initialize(a_bzrdir, name=name,
 
2676
                    append_revisions_only=append_revisions_only)
2113
2677
        if response[0] != 'ok':
2114
2678
            raise errors.UnexpectedSmartServerResponse(response)
2115
2679
        # Turn the response into a RemoteRepository object.
2116
2680
        format = RemoteBranchFormat(network_name=response[1])
2117
2681
        repo_format = response_tuple_to_repo_format(response[3:])
2118
 
        if response[2] == '':
2119
 
            repo_bzrdir = a_bzrdir
 
2682
        repo_path = response[2]
 
2683
        if repository is not None:
 
2684
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
 
2685
            url_diff = urlutils.relative_url(repository.user_url,
 
2686
                    remote_repo_url)
 
2687
            if url_diff != '.':
 
2688
                raise AssertionError(
 
2689
                    'repository.user_url %r does not match URL from server '
 
2690
                    'response (%r + %r)'
 
2691
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
 
2692
            remote_repo = repository
2120
2693
        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)
 
2694
            if repo_path == '':
 
2695
                repo_bzrdir = a_bzrdir
 
2696
            else:
 
2697
                repo_bzrdir = RemoteBzrDir(
 
2698
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
 
2699
                    a_bzrdir._client)
 
2700
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2125
2701
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2126
2702
            format=format, setup_stacking=False, name=name)
 
2703
        if append_revisions_only:
 
2704
            remote_branch.set_append_revisions_only(append_revisions_only)
2127
2705
        # XXX: We know this is a new branch, so it must have revno 0, revid
2128
2706
        # NULL_REVISION. Creating the branch locked would make this be unable
2129
2707
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
2726
        self._ensure_real()
2149
2727
        return self._custom_format.supports_set_append_revisions_only()
2150
2728
 
 
2729
    def _use_default_local_heads_to_fetch(self):
 
2730
        # If the branch format is a metadir format *and* its heads_to_fetch
 
2731
        # implementation is not overridden vs the base class, we can use the
 
2732
        # base class logic rather than use the heads_to_fetch RPC.  This is
 
2733
        # usually cheaper in terms of net round trips, as the last-revision and
 
2734
        # tags info fetched is cached and would be fetched anyway.
 
2735
        self._ensure_real()
 
2736
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
 
2737
            branch_class = self._custom_format._branch_class()
 
2738
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
 
2739
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
 
2740
                return True
 
2741
        return False
 
2742
 
 
2743
 
 
2744
class RemoteBranchStore(config.IniFileStore):
 
2745
    """Branch store which attempts to use HPSS calls to retrieve branch store.
 
2746
 
 
2747
    Note that this is specific to bzr-based formats.
 
2748
    """
 
2749
 
 
2750
    def __init__(self, branch):
 
2751
        super(RemoteBranchStore, self).__init__()
 
2752
        self.branch = branch
 
2753
        self.id = "branch"
 
2754
        self._real_store = None
 
2755
 
 
2756
    def lock_write(self, token=None):
 
2757
        return self.branch.lock_write(token)
 
2758
 
 
2759
    def unlock(self):
 
2760
        return self.branch.unlock()
 
2761
 
 
2762
    @needs_write_lock
 
2763
    def save(self):
 
2764
        # We need to be able to override the undecorated implementation
 
2765
        self.save_without_locking()
 
2766
 
 
2767
    def save_without_locking(self):
 
2768
        super(RemoteBranchStore, self).save()
 
2769
 
 
2770
    def external_url(self):
 
2771
        return self.branch.user_url
 
2772
 
 
2773
    def _load_content(self):
 
2774
        path = self.branch._remote_path()
 
2775
        try:
 
2776
            response, handler = self.branch._call_expecting_body(
 
2777
                'Branch.get_config_file', path)
 
2778
        except errors.UnknownSmartMethod:
 
2779
            self._ensure_real()
 
2780
            return self._real_store._load_content()
 
2781
        if len(response) and response[0] != 'ok':
 
2782
            raise errors.UnexpectedSmartServerResponse(response)
 
2783
        return handler.read_body_bytes()
 
2784
 
 
2785
    def _save_content(self, content):
 
2786
        path = self.branch._remote_path()
 
2787
        try:
 
2788
            response, handler = self.branch._call_with_body_bytes_expecting_body(
 
2789
                'Branch.put_config_file', (path,
 
2790
                    self.branch._lock_token, self.branch._repo_lock_token),
 
2791
                content)
 
2792
        except errors.UnknownSmartMethod:
 
2793
            self._ensure_real()
 
2794
            return self._real_store._save_content(content)
 
2795
        handler.cancel_read_body()
 
2796
        if response != ('ok', ):
 
2797
            raise errors.UnexpectedSmartServerResponse(response)
 
2798
 
 
2799
    def _ensure_real(self):
 
2800
        self.branch._ensure_real()
 
2801
        if self._real_store is None:
 
2802
            self._real_store = config.BranchStore(self.branch)
 
2803
 
2151
2804
 
2152
2805
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
2806
    """Branch stored on a server accessed by HPSS RPC.
2243
2896
    def _get_config(self):
2244
2897
        return RemoteBranchConfig(self)
2245
2898
 
 
2899
    def _get_config_store(self):
 
2900
        return RemoteBranchStore(self)
 
2901
 
2246
2902
    def _get_real_transport(self):
2247
2903
        # if we try vfs access, return the real branch's vfs transport
2248
2904
        self._ensure_real()
2311
2967
                self.bzrdir, self._client)
2312
2968
        return self._control_files
2313
2969
 
2314
 
    def _get_checkout_format(self):
 
2970
    def _get_checkout_format(self, lightweight=False):
2315
2971
        self._ensure_real()
2316
 
        return self._real_branch._get_checkout_format()
 
2972
        if lightweight:
 
2973
            format = RemoteBzrDirFormat()
 
2974
            self.bzrdir._format._supply_sub_formats_to(format)
 
2975
            format.workingtree_format = self._real_branch._get_checkout_format(
 
2976
                lightweight=lightweight).workingtree_format
 
2977
            return format
 
2978
        else:
 
2979
            return self._real_branch._get_checkout_format(lightweight=False)
2317
2980
 
2318
2981
    def get_physical_lock_status(self):
2319
2982
        """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()
 
2983
        try:
 
2984
            response = self._client.call('Branch.get_physical_lock_status',
 
2985
                self._remote_path())
 
2986
        except errors.UnknownSmartMethod:
 
2987
            self._ensure_real()
 
2988
            return self._real_branch.get_physical_lock_status()
 
2989
        if response[0] not in ('yes', 'no'):
 
2990
            raise errors.UnexpectedSmartServerResponse(response)
 
2991
        return (response[0] == 'yes')
2323
2992
 
2324
2993
    def get_stacked_on_url(self):
2325
2994
        """Get the URL this branch is stacked against.
2352
3021
            self._is_stacked = False
2353
3022
        else:
2354
3023
            self._is_stacked = True
2355
 
        
 
3024
 
2356
3025
    def _vfs_get_tags_bytes(self):
2357
3026
        self._ensure_real()
2358
3027
        return self._real_branch._get_tags_bytes()
2359
3028
 
 
3029
    @needs_read_lock
2360
3030
    def _get_tags_bytes(self):
 
3031
        if self._tags_bytes is None:
 
3032
            self._tags_bytes = self._get_tags_bytes_via_hpss()
 
3033
        return self._tags_bytes
 
3034
 
 
3035
    def _get_tags_bytes_via_hpss(self):
2361
3036
        medium = self._client._medium
2362
3037
        if medium._is_remote_before((1, 13)):
2363
3038
            return self._vfs_get_tags_bytes()
2373
3048
        return self._real_branch._set_tags_bytes(bytes)
2374
3049
 
2375
3050
    def _set_tags_bytes(self, bytes):
 
3051
        if self.is_locked():
 
3052
            self._tags_bytes = bytes
2376
3053
        medium = self._client._medium
2377
3054
        if medium._is_remote_before((1, 18)):
2378
3055
            self._vfs_set_tags_bytes(bytes)
2387
3064
            self._vfs_set_tags_bytes(bytes)
2388
3065
 
2389
3066
    def lock_read(self):
 
3067
        """Lock the branch for read operations.
 
3068
 
 
3069
        :return: A bzrlib.lock.LogicalLockResult.
 
3070
        """
2390
3071
        self.repository.lock_read()
2391
3072
        if not self._lock_mode:
2392
3073
            self._note_lock('r')
2396
3077
                self._real_branch.lock_read()
2397
3078
        else:
2398
3079
            self._lock_count += 1
 
3080
        return lock.LogicalLockResult(self.unlock)
2399
3081
 
2400
3082
    def _remote_lock_write(self, token):
2401
3083
        if token is None:
2402
3084
            branch_token = repo_token = ''
2403
3085
        else:
2404
3086
            branch_token = token
2405
 
            repo_token = self.repository.lock_write()
 
3087
            repo_token = self.repository.lock_write().repository_token
2406
3088
            self.repository.unlock()
2407
3089
        err_context = {'token': token}
2408
 
        response = self._call(
2409
 
            'Branch.lock_write', self._remote_path(), branch_token,
2410
 
            repo_token or '', **err_context)
 
3090
        try:
 
3091
            response = self._call(
 
3092
                'Branch.lock_write', self._remote_path(), branch_token,
 
3093
                repo_token or '', **err_context)
 
3094
        except errors.LockContention, e:
 
3095
            # The LockContention from the server doesn't have any
 
3096
            # information about the lock_url. We re-raise LockContention
 
3097
            # with valid lock_url.
 
3098
            raise errors.LockContention('(remote lock)',
 
3099
                self.repository.base.split('.bzr/')[0])
2411
3100
        if response[0] != 'ok':
2412
3101
            raise errors.UnexpectedSmartServerResponse(response)
2413
3102
        ok, branch_token, repo_token = response
2434
3123
            self._lock_mode = 'w'
2435
3124
            self._lock_count = 1
2436
3125
        elif self._lock_mode == 'r':
2437
 
            raise errors.ReadOnlyTransaction
 
3126
            raise errors.ReadOnlyError(self)
2438
3127
        else:
2439
3128
            if token is not None:
2440
3129
                # A token was given to lock_write, and we're relocking, so
2445
3134
            self._lock_count += 1
2446
3135
            # Re-lock the repository too.
2447
3136
            self.repository.lock_write(self._repo_lock_token)
2448
 
        return self._lock_token or None
 
3137
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
2449
3138
 
2450
3139
    def _unlock(self, branch_token, repo_token):
2451
3140
        err_context = {'token': str((branch_token, repo_token))}
2489
3178
            self.repository.unlock()
2490
3179
 
2491
3180
    def break_lock(self):
2492
 
        self._ensure_real()
2493
 
        return self._real_branch.break_lock()
 
3181
        try:
 
3182
            response = self._call(
 
3183
                'Branch.break_lock', self._remote_path())
 
3184
        except errors.UnknownSmartMethod:
 
3185
            self._ensure_real()
 
3186
            return self._real_branch.break_lock()
 
3187
        if response != ('ok',):
 
3188
            raise errors.UnexpectedSmartServerResponse(response)
2494
3189
 
2495
3190
    def leave_lock_in_place(self):
2496
3191
        if not self._lock_token:
2520
3215
            missing_parent = parent_map[missing_parent]
2521
3216
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2522
3217
 
2523
 
    def _last_revision_info(self):
 
3218
    def _read_last_revision_info(self):
2524
3219
        response = self._call('Branch.last_revision_info', self._remote_path())
2525
3220
        if response[0] != 'ok':
2526
3221
            raise SmartProtocolError('unexpected response code %s' % (response,))
2589
3284
            raise errors.UnexpectedSmartServerResponse(response)
2590
3285
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2591
3286
 
 
3287
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2592
3288
    @needs_write_lock
2593
3289
    def set_revision_history(self, rev_history):
 
3290
        """See Branch.set_revision_history."""
 
3291
        self._set_revision_history(rev_history)
 
3292
 
 
3293
    @needs_write_lock
 
3294
    def _set_revision_history(self, rev_history):
2594
3295
        # Send just the tip revision of the history; the server will generate
2595
3296
        # the full history from that.  If the revision doesn't exist in this
2596
3297
        # branch, NoSuchRevision will be raised.
2654
3355
            _override_hook_target=self, **kwargs)
2655
3356
 
2656
3357
    @needs_read_lock
2657
 
    def push(self, target, overwrite=False, stop_revision=None):
 
3358
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
2658
3359
        self._ensure_real()
2659
3360
        return self._real_branch.push(
2660
 
            target, overwrite=overwrite, stop_revision=stop_revision,
 
3361
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
2661
3362
            _override_hook_source_branch=self)
2662
3363
 
2663
3364
    def is_locked(self):
2664
3365
        return self._lock_count >= 1
2665
3366
 
2666
3367
    @needs_read_lock
 
3368
    def revision_id_to_dotted_revno(self, revision_id):
 
3369
        """Given a revision id, return its dotted revno.
 
3370
 
 
3371
        :return: a tuple like (1,) or (400,1,3).
 
3372
        """
 
3373
        try:
 
3374
            response = self._call('Branch.revision_id_to_revno',
 
3375
                self._remote_path(), revision_id)
 
3376
        except errors.UnknownSmartMethod:
 
3377
            self._ensure_real()
 
3378
            return self._real_branch.revision_id_to_dotted_revno(revision_id)
 
3379
        if response[0] == 'ok':
 
3380
            return tuple([int(x) for x in response[1:]])
 
3381
        else:
 
3382
            raise errors.UnexpectedSmartServerResponse(response)
 
3383
 
 
3384
    @needs_read_lock
2667
3385
    def revision_id_to_revno(self, revision_id):
2668
 
        self._ensure_real()
2669
 
        return self._real_branch.revision_id_to_revno(revision_id)
 
3386
        """Given a revision id on the branch mainline, return its revno.
 
3387
 
 
3388
        :return: an integer
 
3389
        """
 
3390
        try:
 
3391
            response = self._call('Branch.revision_id_to_revno',
 
3392
                self._remote_path(), revision_id)
 
3393
        except errors.UnknownSmartMethod:
 
3394
            self._ensure_real()
 
3395
            return self._real_branch.revision_id_to_revno(revision_id)
 
3396
        if response[0] == 'ok':
 
3397
            if len(response) == 2:
 
3398
                return int(response[1])
 
3399
            raise NoSuchRevision(self, revision_id)
 
3400
        else:
 
3401
            raise errors.UnexpectedSmartServerResponse(response)
2670
3402
 
2671
3403
    @needs_write_lock
2672
3404
    def set_last_revision_info(self, revno, revision_id):
2673
3405
        # XXX: These should be returned by the set_last_revision_info verb
2674
3406
        old_revno, old_revid = self.last_revision_info()
2675
3407
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
 
        revision_id = ensure_null(revision_id)
 
3408
        if not revision_id or not isinstance(revision_id, basestring):
 
3409
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2677
3410
        try:
2678
3411
            response = self._call('Branch.set_last_revision_info',
2679
3412
                self._remote_path(), self._lock_token, self._repo_lock_token,
2708
3441
            except errors.UnknownSmartMethod:
2709
3442
                medium._remember_remote_is_before((1, 6))
2710
3443
        self._clear_cached_state_of_remote_branch_only()
2711
 
        self.set_revision_history(self._lefthand_history(revision_id,
 
3444
        self._set_revision_history(self._lefthand_history(revision_id,
2712
3445
            last_rev=last_rev,other_branch=other_branch))
2713
3446
 
2714
3447
    def set_push_location(self, location):
2715
3448
        self._ensure_real()
2716
3449
        return self._real_branch.set_push_location(location)
2717
3450
 
 
3451
    def heads_to_fetch(self):
 
3452
        if self._format._use_default_local_heads_to_fetch():
 
3453
            # We recognise this format, and its heads-to-fetch implementation
 
3454
            # is the default one (tip + tags).  In this case it's cheaper to
 
3455
            # just use the default implementation rather than a special RPC as
 
3456
            # the tip and tags data is cached.
 
3457
            return branch.Branch.heads_to_fetch(self)
 
3458
        medium = self._client._medium
 
3459
        if medium._is_remote_before((2, 4)):
 
3460
            return self._vfs_heads_to_fetch()
 
3461
        try:
 
3462
            return self._rpc_heads_to_fetch()
 
3463
        except errors.UnknownSmartMethod:
 
3464
            medium._remember_remote_is_before((2, 4))
 
3465
            return self._vfs_heads_to_fetch()
 
3466
 
 
3467
    def _rpc_heads_to_fetch(self):
 
3468
        response = self._call('Branch.heads_to_fetch', self._remote_path())
 
3469
        if len(response) != 2:
 
3470
            raise errors.UnexpectedSmartServerResponse(response)
 
3471
        must_fetch, if_present_fetch = response
 
3472
        return set(must_fetch), set(if_present_fetch)
 
3473
 
 
3474
    def _vfs_heads_to_fetch(self):
 
3475
        self._ensure_real()
 
3476
        return self._real_branch.heads_to_fetch()
 
3477
 
2718
3478
 
2719
3479
class RemoteConfig(object):
2720
3480
    """A Config that reads and writes from smart verbs.
2734
3494
        """
2735
3495
        try:
2736
3496
            configobj = self._get_configobj()
 
3497
            section_obj = None
2737
3498
            if section is None:
2738
3499
                section_obj = configobj
2739
3500
            else:
2740
3501
                try:
2741
3502
                    section_obj = configobj[section]
2742
3503
                except KeyError:
2743
 
                    return default
2744
 
            return section_obj.get(name, default)
 
3504
                    pass
 
3505
            if section_obj is None:
 
3506
                value = default
 
3507
            else:
 
3508
                value = section_obj.get(name, default)
2745
3509
        except errors.UnknownSmartMethod:
2746
 
            return self._vfs_get_option(name, section, default)
 
3510
            value = self._vfs_get_option(name, section, default)
 
3511
        for hook in config.OldConfigHooks['get']:
 
3512
            hook(self, name, value)
 
3513
        return value
2747
3514
 
2748
3515
    def _response_to_configobj(self, response):
2749
3516
        if len(response[0]) and response[0][0] != 'ok':
2750
3517
            raise errors.UnexpectedSmartServerResponse(response)
2751
3518
        lines = response[1].read_body_bytes().splitlines()
2752
 
        return config.ConfigObj(lines, encoding='utf-8')
 
3519
        conf = config.ConfigObj(lines, encoding='utf-8')
 
3520
        for hook in config.OldConfigHooks['load']:
 
3521
            hook(self)
 
3522
        return conf
2753
3523
 
2754
3524
 
2755
3525
class RemoteBranchConfig(RemoteConfig):
2774
3544
        medium = self._branch._client._medium
2775
3545
        if medium._is_remote_before((1, 14)):
2776
3546
            return self._vfs_set_option(value, name, section)
 
3547
        if isinstance(value, dict):
 
3548
            if medium._is_remote_before((2, 2)):
 
3549
                return self._vfs_set_option(value, name, section)
 
3550
            return self._set_config_option_dict(value, name, section)
 
3551
        else:
 
3552
            return self._set_config_option(value, name, section)
 
3553
 
 
3554
    def _set_config_option(self, value, name, section):
2777
3555
        try:
2778
3556
            path = self._branch._remote_path()
2779
3557
            response = self._branch._client.call('Branch.set_config_option',
2780
3558
                path, self._branch._lock_token, self._branch._repo_lock_token,
2781
3559
                value.encode('utf8'), name, section or '')
2782
3560
        except errors.UnknownSmartMethod:
 
3561
            medium = self._branch._client._medium
2783
3562
            medium._remember_remote_is_before((1, 14))
2784
3563
            return self._vfs_set_option(value, name, section)
2785
3564
        if response != ():
2786
3565
            raise errors.UnexpectedSmartServerResponse(response)
2787
3566
 
 
3567
    def _serialize_option_dict(self, option_dict):
 
3568
        utf8_dict = {}
 
3569
        for key, value in option_dict.items():
 
3570
            if isinstance(key, unicode):
 
3571
                key = key.encode('utf8')
 
3572
            if isinstance(value, unicode):
 
3573
                value = value.encode('utf8')
 
3574
            utf8_dict[key] = value
 
3575
        return bencode.bencode(utf8_dict)
 
3576
 
 
3577
    def _set_config_option_dict(self, value, name, section):
 
3578
        try:
 
3579
            path = self._branch._remote_path()
 
3580
            serialised_dict = self._serialize_option_dict(value)
 
3581
            response = self._branch._client.call(
 
3582
                'Branch.set_config_option_dict',
 
3583
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
3584
                serialised_dict, name, section or '')
 
3585
        except errors.UnknownSmartMethod:
 
3586
            medium = self._branch._client._medium
 
3587
            medium._remember_remote_is_before((2, 2))
 
3588
            return self._vfs_set_option(value, name, section)
 
3589
        if response != ():
 
3590
            raise errors.UnexpectedSmartServerResponse(response)
 
3591
 
2788
3592
    def _real_object(self):
2789
3593
        self._branch._ensure_real()
2790
3594
        return self._branch._real_branch
2839
3643
        tar.extract(tarinfo, to_dir)
2840
3644
 
2841
3645
 
 
3646
error_translators = registry.Registry()
 
3647
no_context_error_translators = registry.Registry()
 
3648
 
 
3649
 
2842
3650
def _translate_error(err, **context):
2843
3651
    """Translate an ErrorFromSmartServer into a more useful error.
2844
3652
 
2873
3681
                    'Missing key %r in context %r', key_err.args[0], context)
2874
3682
                raise err
2875
3683
 
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'):
 
3684
    try:
 
3685
        translator = error_translators.get(err.error_verb)
 
3686
    except KeyError:
 
3687
        pass
 
3688
    else:
 
3689
        raise translator(err, find, get_path)
 
3690
    try:
 
3691
        translator = no_context_error_translators.get(err.error_verb)
 
3692
    except KeyError:
 
3693
        raise errors.UnknownErrorFromSmartServer(err)
 
3694
    else:
 
3695
        raise translator(err)
 
3696
 
 
3697
 
 
3698
error_translators.register('NoSuchRevision',
 
3699
    lambda err, find, get_path: NoSuchRevision(
 
3700
        find('branch'), err.error_args[0]))
 
3701
error_translators.register('nosuchrevision',
 
3702
    lambda err, find, get_path: NoSuchRevision(
 
3703
        find('repository'), err.error_args[0]))
 
3704
 
 
3705
def _translate_nobranch_error(err, find, get_path):
 
3706
    if len(err.error_args) >= 1:
 
3707
        extra = err.error_args[0]
 
3708
    else:
 
3709
        extra = None
 
3710
    return errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
3711
        detail=extra)
 
3712
 
 
3713
error_translators.register('nobranch', _translate_nobranch_error)
 
3714
error_translators.register('norepository',
 
3715
    lambda err, find, get_path: errors.NoRepositoryPresent(
 
3716
        find('bzrdir')))
 
3717
error_translators.register('UnlockableTransport',
 
3718
    lambda err, find, get_path: errors.UnlockableTransport(
 
3719
        find('bzrdir').root_transport))
 
3720
error_translators.register('TokenMismatch',
 
3721
    lambda err, find, get_path: errors.TokenMismatch(
 
3722
        find('token'), '(remote token)'))
 
3723
error_translators.register('Diverged',
 
3724
    lambda err, find, get_path: errors.DivergedBranches(
 
3725
        find('branch'), find('other_branch')))
 
3726
error_translators.register('NotStacked',
 
3727
    lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
 
3728
 
 
3729
def _translate_PermissionDenied(err, find, get_path):
 
3730
    path = get_path()
 
3731
    if len(err.error_args) >= 2:
 
3732
        extra = err.error_args[1]
 
3733
    else:
 
3734
        extra = None
 
3735
    return errors.PermissionDenied(path, extra=extra)
 
3736
 
 
3737
error_translators.register('PermissionDenied', _translate_PermissionDenied)
 
3738
error_translators.register('ReadError',
 
3739
    lambda err, find, get_path: errors.ReadError(get_path()))
 
3740
error_translators.register('NoSuchFile',
 
3741
    lambda err, find, get_path: errors.NoSuchFile(get_path()))
 
3742
no_context_error_translators.register('IncompatibleRepositories',
 
3743
    lambda err: errors.IncompatibleRepositories(
 
3744
        err.error_args[0], err.error_args[1], err.error_args[2]))
 
3745
no_context_error_translators.register('LockContention',
 
3746
    lambda err: errors.LockContention('(remote lock)'))
 
3747
no_context_error_translators.register('LockFailed',
 
3748
    lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
 
3749
no_context_error_translators.register('TipChangeRejected',
 
3750
    lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
 
3751
no_context_error_translators.register('UnstackableBranchFormat',
 
3752
    lambda err: errors.UnstackableBranchFormat(*err.error_args))
 
3753
no_context_error_translators.register('UnstackableRepositoryFormat',
 
3754
    lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
 
3755
no_context_error_translators.register('FileExists',
 
3756
    lambda err: errors.FileExists(err.error_args[0]))
 
3757
no_context_error_translators.register('DirectoryNotEmpty',
 
3758
    lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
 
3759
 
 
3760
def _translate_short_readv_error(err):
 
3761
    args = err.error_args
 
3762
    return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
 
3763
        int(args[3]))
 
3764
 
 
3765
no_context_error_translators.register('ShortReadvError',
 
3766
    _translate_short_readv_error)
 
3767
 
 
3768
def _translate_unicode_error(err):
2932
3769
        encoding = str(err.error_args[0]) # encoding must always be a string
2933
3770
        val = err.error_args[1]
2934
3771
        start = int(err.error_args[2])
2942
3779
            raise UnicodeDecodeError(encoding, val, start, end, reason)
2943
3780
        elif err.error_verb == 'UnicodeEncodeError':
2944
3781
            raise UnicodeEncodeError(encoding, val, start, end, reason)
2945
 
    elif err.error_verb == 'ReadOnlyError':
2946
 
        raise errors.TransportNotPossible('readonly transport')
2947
 
    raise errors.UnknownErrorFromSmartServer(err)
 
3782
 
 
3783
no_context_error_translators.register('UnicodeEncodeError',
 
3784
    _translate_unicode_error)
 
3785
no_context_error_translators.register('UnicodeDecodeError',
 
3786
    _translate_unicode_error)
 
3787
no_context_error_translators.register('ReadOnlyError',
 
3788
    lambda err: errors.TransportNotPossible('readonly transport'))
 
3789
no_context_error_translators.register('MemoryError',
 
3790
    lambda err: errors.BzrError("remote server out of memory\n"
 
3791
        "Retry non-remotely, or contact the server admin for details."))
 
3792
 
 
3793
no_context_error_translators.register('BzrCheckError',
 
3794
    lambda err: errors.BzrCheckError(msg=err.error_args[0]))
 
3795
 
 
3796
error_translators.register('UnsuspendableWriteGroup',
 
3797
    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
 
3798
        repository=find('repository')))
 
3799
error_translators.register('UnresumableWriteGroup',
 
3800
    lambda err, find, get_path: errors.UnresumableWriteGroup(
 
3801
        repository=find('repository'), write_groups=err.error_args[0],
 
3802
        reason=err.error_args[1]))