/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 brzlib/remote.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
17
19
import bz2
18
 
import os
19
 
import re
20
 
import sys
21
20
import zlib
22
21
 
23
 
from .. import (
 
22
from brzlib import (
24
23
    bencode,
25
24
    branch,
26
 
    bzr as _mod_bzr,
 
25
    bzrdir as _mod_bzrdir,
27
26
    config as _mod_config,
28
27
    controldir,
29
28
    debug,
30
29
    errors,
31
30
    gpg,
32
31
    graph,
 
32
    inventory_delta,
33
33
    lock,
34
34
    lockdir,
35
35
    osutils,
36
36
    registry,
37
37
    repository as _mod_repository,
38
38
    revision as _mod_revision,
 
39
    static_tuple,
 
40
    symbol_versioning,
 
41
    testament as _mod_testament,
39
42
    urlutils,
40
 
    )
41
 
from . import (
42
 
    branch as bzrbranch,
43
 
    bzrdir as _mod_bzrdir,
44
 
    inventory_delta,
45
 
    testament as _mod_testament,
46
43
    vf_repository,
47
44
    vf_search,
48
45
    )
49
 
from .branch import BranchReferenceFormat
50
 
from ..branch import BranchWriteLockResult
51
 
from ..decorators import only_raises
52
 
from ..errors import (
 
46
from brzlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
47
from brzlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
48
from brzlib.errors import (
53
49
    NoSuchRevision,
54
50
    SmartProtocolError,
55
51
    )
56
 
from ..i18n import gettext
57
 
from .inventory import Inventory
58
 
from .inventorytree import InventoryRevisionTree
59
 
from ..lockable_files import LockableFiles
60
 
from .smart import client, vfs, repository as smart_repo
61
 
from .smart.client import _SmartClient
62
 
from ..revision import NULL_REVISION
63
 
from ..repository import RepositoryWriteLockResult, _LazyListJoin
64
 
from .serializer import format_registry as serializer_format_registry
65
 
from ..trace import mutter, note, warning, log_exception_quietly
66
 
from .versionedfile import FulltextContentFactory
 
52
from brzlib.i18n import gettext
 
53
from brzlib.inventory import Inventory
 
54
from brzlib.lockable_files import LockableFiles
 
55
from brzlib.smart import client, vfs, repository as smart_repo
 
56
from brzlib.smart.client import _SmartClient
 
57
from brzlib.revision import NULL_REVISION
 
58
from brzlib.revisiontree import InventoryRevisionTree
 
59
from brzlib.repository import RepositoryWriteLockResult, _LazyListJoin
 
60
from brzlib.serializer import format_registry as serializer_format_registry
 
61
from brzlib.trace import mutter, note, warning, log_exception_quietly
 
62
from brzlib.versionedfile import FulltextContentFactory
67
63
 
68
64
 
69
65
_DEFAULT_SEARCH_DEPTH = 100
75
71
    def _call(self, method, *args, **err_context):
76
72
        try:
77
73
            return self._client.call(method, *args)
78
 
        except errors.ErrorFromSmartServer as err:
 
74
        except errors.ErrorFromSmartServer, err:
79
75
            self._translate_error(err, **err_context)
80
76
 
81
77
    def _call_expecting_body(self, method, *args, **err_context):
82
78
        try:
83
79
            return self._client.call_expecting_body(method, *args)
84
 
        except errors.ErrorFromSmartServer as err:
 
80
        except errors.ErrorFromSmartServer, err:
85
81
            self._translate_error(err, **err_context)
86
82
 
87
83
    def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
88
84
        try:
89
85
            return self._client.call_with_body_bytes(method, args, body_bytes)
90
 
        except errors.ErrorFromSmartServer as err:
 
86
        except errors.ErrorFromSmartServer, err:
91
87
            self._translate_error(err, **err_context)
92
88
 
93
89
    def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
95
91
        try:
96
92
            return self._client.call_with_body_bytes_expecting_body(
97
93
                method, args, body_bytes)
98
 
        except errors.ErrorFromSmartServer as err:
 
94
        except errors.ErrorFromSmartServer, err:
99
95
            self._translate_error(err, **err_context)
100
96
 
101
97
 
102
98
def response_tuple_to_repo_format(response):
103
99
    """Convert a response tuple describing a repository format to a format."""
104
100
    format = RemoteRepositoryFormat()
105
 
    format._rich_root_data = (response[0] == b'yes')
106
 
    format._supports_tree_reference = (response[1] == b'yes')
107
 
    format._supports_external_lookups = (response[2] == b'yes')
 
101
    format._rich_root_data = (response[0] == 'yes')
 
102
    format._supports_tree_reference = (response[1] == 'yes')
 
103
    format._supports_external_lookups = (response[2] == 'yes')
108
104
    format._network_name = response[3]
109
105
    return format
110
106
 
111
107
 
112
 
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
 
108
# Note that RemoteBzrDirProber lives in brzlib.bzrdir so brzlib.remote
113
109
# does not have to be imported unless a remote format is involved.
114
110
 
115
111
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
129
125
 
130
126
    def __repr__(self):
131
127
        return "%s(_network_name=%r)" % (self.__class__.__name__,
132
 
                                         self._network_name)
 
128
            self._network_name)
133
129
 
134
130
    def get_format_description(self):
135
131
        if self._network_name:
136
132
            try:
137
133
                real_format = controldir.network_format_registry.get(
138
 
                    self._network_name)
 
134
                        self._network_name)
139
135
            except KeyError:
140
136
                pass
141
137
            else:
162
158
        client = _SmartClient(client_medium)
163
159
        path = client.remote_path_from_transport(transport)
164
160
        try:
165
 
            response = client.call(b'BzrDirFormat.initialize', path)
166
 
        except errors.ErrorFromSmartServer as err:
 
161
            response = client.call('BzrDirFormat.initialize', path)
 
162
        except errors.ErrorFromSmartServer, err:
167
163
            _translate_error(err, path=path)
168
 
        if response[0] != b'ok':
169
 
            raise errors.SmartProtocolError(
170
 
                'unexpected response code %s' % (response,))
 
164
        if response[0] != 'ok':
 
165
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
171
166
        format = RemoteBzrDirFormat()
172
167
        self._supply_sub_formats_to(format)
173
168
        return RemoteBzrDir(transport, format)
175
170
    def parse_NoneTrueFalse(self, arg):
176
171
        if not arg:
177
172
            return None
178
 
        if arg == b'False':
 
173
        if arg == 'False':
179
174
            return False
180
 
        if arg == b'True':
 
175
        if arg == 'True':
181
176
            return True
182
177
        raise AssertionError("invalid arg %r" % arg)
183
178
 
184
179
    def _serialize_NoneTrueFalse(self, arg):
185
180
        if arg is False:
186
 
            return b'False'
 
181
            return 'False'
187
182
        if arg:
188
 
            return b'True'
189
 
        return b''
 
183
            return 'True'
 
184
        return ''
190
185
 
191
186
    def _serialize_NoneString(self, arg):
192
 
        return arg or b''
 
187
        return arg or ''
193
188
 
194
189
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
195
 
                                   create_prefix=False, force_new_repo=False, stacked_on=None,
196
 
                                   stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
197
 
                                   shared_repo=False):
 
190
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
191
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
192
        shared_repo=False):
198
193
        try:
199
194
            # hand off the request to the smart server
200
195
            client_medium = transport.get_smart_medium()
226
221
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
227
222
            self._supply_sub_formats_to(local_dir_format)
228
223
            return local_dir_format.initialize_on_transport_ex(transport,
229
 
                                                               use_existing_dir=use_existing_dir, create_prefix=create_prefix,
230
 
                                                               force_new_repo=force_new_repo, stacked_on=stacked_on,
231
 
                                                               stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
232
 
                                                               make_working_trees=make_working_trees, shared_repo=shared_repo,
233
 
                                                               vfs_only=True)
 
224
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
225
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
226
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
227
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
228
                vfs_only=True)
234
229
        return self._initialize_on_transport_ex_rpc(client, path, transport,
235
 
                                                    use_existing_dir, create_prefix, force_new_repo, stacked_on,
236
 
                                                    stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
 
230
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
231
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
237
232
 
238
233
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
239
 
                                        use_existing_dir, create_prefix, force_new_repo, stacked_on,
240
 
                                        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
 
234
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
235
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
241
236
        args = []
242
237
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
243
238
        args.append(self._serialize_NoneTrueFalse(create_prefix))
246
241
        # stack_on_pwd is often/usually our transport
247
242
        if stack_on_pwd:
248
243
            try:
249
 
                stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
 
244
                stack_on_pwd = transport.relpath(stack_on_pwd)
250
245
                if not stack_on_pwd:
251
 
                    stack_on_pwd = b'.'
 
246
                    stack_on_pwd = '.'
252
247
            except errors.PathNotChild:
253
248
                pass
254
249
        args.append(self._serialize_NoneString(stack_on_pwd))
258
253
        request_network_name = self._network_name or \
259
254
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
260
255
        try:
261
 
            response = client.call(b'BzrDirFormat.initialize_ex_1.16',
262
 
                                   request_network_name, path, *args)
 
256
            response = client.call('BzrDirFormat.initialize_ex_1.16',
 
257
                request_network_name, path, *args)
263
258
        except errors.UnknownSmartMethod:
264
 
            client._medium._remember_remote_is_before((1, 16))
 
259
            client._medium._remember_remote_is_before((1,16))
265
260
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
266
261
            self._supply_sub_formats_to(local_dir_format)
267
262
            return local_dir_format.initialize_on_transport_ex(transport,
268
 
                                                               use_existing_dir=use_existing_dir, create_prefix=create_prefix,
269
 
                                                               force_new_repo=force_new_repo, stacked_on=stacked_on,
270
 
                                                               stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
271
 
                                                               make_working_trees=make_working_trees, shared_repo=shared_repo,
272
 
                                                               vfs_only=True)
273
 
        except errors.ErrorFromSmartServer as err:
274
 
            _translate_error(err, path=path.decode('utf-8'))
 
263
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
264
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
265
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
266
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
267
                vfs_only=True)
 
268
        except errors.ErrorFromSmartServer, err:
 
269
            _translate_error(err, path=path)
275
270
        repo_path = response[0]
276
271
        bzrdir_name = response[6]
277
272
        require_stacking = response[7]
282
277
        bzrdir = RemoteBzrDir(transport, format, _client=client)
283
278
        if repo_path:
284
279
            repo_format = response_tuple_to_repo_format(response[1:])
285
 
            if repo_path == b'.':
286
 
                repo_path = b''
287
 
            repo_path = repo_path.decode('utf-8')
 
280
            if repo_path == '.':
 
281
                repo_path = ''
288
282
            if repo_path:
289
283
                repo_bzrdir_format = RemoteBzrDirFormat()
290
284
                repo_bzrdir_format._network_name = response[5]
291
285
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
292
 
                                        repo_bzrdir_format)
 
286
                    repo_bzrdir_format)
293
287
            else:
294
288
                repo_bzr = bzrdir
295
289
            final_stack = response[8] or None
296
 
            if final_stack:
297
 
                final_stack = final_stack.decode('utf-8')
298
290
            final_stack_pwd = response[9] or None
299
291
            if final_stack_pwd:
300
292
                final_stack_pwd = urlutils.join(
301
 
                    transport.base, final_stack_pwd.decode('utf-8'))
 
293
                    transport.base, final_stack_pwd)
302
294
            remote_repo = RemoteRepository(repo_bzr, repo_format)
303
295
            if len(response) > 10:
304
296
                # Updated server verb that locks remotely.
308
300
                    remote_repo.dont_leave_lock_in_place()
309
301
            else:
310
302
                remote_repo.lock_write()
311
 
            policy = _mod_bzrdir.UseExistingRepository(remote_repo,
312
 
                                                       final_stack, final_stack_pwd, require_stacking)
 
303
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
 
304
                final_stack_pwd, require_stacking)
313
305
            policy.acquire_repository()
314
306
        else:
315
307
            remote_repo = None
356
348
        return result
357
349
 
358
350
    repository_format = property(__return_repository_format,
359
 
                                 _mod_bzrdir.BzrDirMetaFormat1._set_repository_format)  # .im_func)
 
351
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
360
352
 
361
353
 
362
354
class RemoteControlStore(_mod_config.IniFileStore):
367
359
 
368
360
    def __init__(self, bzrdir):
369
361
        super(RemoteControlStore, self).__init__()
370
 
        self.controldir = bzrdir
 
362
        self.bzrdir = bzrdir
371
363
        self._real_store = None
372
364
 
373
365
    def lock_write(self, token=None):
378
370
        self._ensure_real()
379
371
        return self._real_store.unlock()
380
372
 
 
373
    @needs_write_lock
381
374
    def save(self):
382
 
        with self.lock_write():
383
 
            # We need to be able to override the undecorated implementation
384
 
            self.save_without_locking()
 
375
        # We need to be able to override the undecorated implementation
 
376
        self.save_without_locking()
385
377
 
386
378
    def save_without_locking(self):
387
379
        super(RemoteControlStore, self).save()
388
380
 
389
381
    def _ensure_real(self):
390
 
        self.controldir._ensure_real()
 
382
        self.bzrdir._ensure_real()
391
383
        if self._real_store is None:
392
 
            self._real_store = _mod_config.ControlStore(self.controldir)
 
384
            self._real_store = _mod_config.ControlStore(self.bzrdir)
393
385
 
394
386
    def external_url(self):
395
387
        return urlutils.join(self.branch.user_url, 'control.conf')
396
388
 
397
389
    def _load_content(self):
398
 
        medium = self.controldir._client._medium
399
 
        path = self.controldir._path_for_remote_call(self.controldir._client)
 
390
        medium = self.bzrdir._client._medium
 
391
        path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
400
392
        try:
401
 
            response, handler = self.controldir._call_expecting_body(
402
 
                b'BzrDir.get_config_file', path)
 
393
            response, handler = self.bzrdir._call_expecting_body(
 
394
                'BzrDir.get_config_file', path)
403
395
        except errors.UnknownSmartMethod:
404
396
            self._ensure_real()
405
397
            return self._real_store._load_content()
406
 
        if len(response) and response[0] != b'ok':
 
398
        if len(response) and response[0] != 'ok':
407
399
            raise errors.UnexpectedSmartServerResponse(response)
408
400
        return handler.read_body_bytes()
409
401
 
460
452
            self._rpc_open(path)
461
453
 
462
454
    def _rpc_open_2_1(self, path):
463
 
        response = self._call(b'BzrDir.open_2.1', path)
464
 
        if response == (b'no',):
 
455
        response = self._call('BzrDir.open_2.1', path)
 
456
        if response == ('no',):
465
457
            raise errors.NotBranchError(path=self.root_transport.base)
466
 
        elif response[0] == b'yes':
467
 
            if response[1] == b'yes':
 
458
        elif response[0] == 'yes':
 
459
            if response[1] == 'yes':
468
460
                self._has_working_tree = True
469
 
            elif response[1] == b'no':
 
461
            elif response[1] == 'no':
470
462
                self._has_working_tree = False
471
463
            else:
472
464
                raise errors.UnexpectedSmartServerResponse(response)
474
466
            raise errors.UnexpectedSmartServerResponse(response)
475
467
 
476
468
    def _rpc_open(self, path):
477
 
        response = self._call(b'BzrDir.open', path)
478
 
        if response not in [(b'yes',), (b'no',)]:
 
469
        response = self._call('BzrDir.open', path)
 
470
        if response not in [('yes',), ('no',)]:
479
471
            raise errors.UnexpectedSmartServerResponse(response)
480
 
        if response == (b'no',):
 
472
        if response == ('no',):
481
473
            raise errors.NotBranchError(path=self.root_transport.base)
482
474
 
483
475
    def _ensure_real(self):
489
481
            if 'hpssvfs' in debug.debug_flags:
490
482
                import traceback
491
483
                warning('VFS BzrDir access triggered\n%s',
492
 
                        ''.join(traceback.format_stack()))
 
484
                    ''.join(traceback.format_stack()))
493
485
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
494
 
                self.root_transport, probers=[_mod_bzr.BzrProber])
 
486
                self.root_transport, probers=[_mod_bzrdir.BzrProber])
495
487
            self._format._network_name = \
496
488
                self._real_bzrdir._format.network_name()
497
489
 
516
508
            return self._vfs_checkout_metadir()
517
509
        path = self._path_for_remote_call(self._client)
518
510
        try:
519
 
            response = self._client.call(b'BzrDir.checkout_metadir',
520
 
                                         path)
 
511
            response = self._client.call('BzrDir.checkout_metadir',
 
512
                path)
521
513
        except errors.UnknownSmartMethod:
522
514
            medium._remember_remote_is_before((2, 5))
523
515
            return self._vfs_checkout_metadir()
528
520
            format = controldir.network_format_registry.get(control_name)
529
521
        except KeyError:
530
522
            raise errors.UnknownFormatError(kind='control',
531
 
                                            format=control_name)
 
523
                format=control_name)
532
524
        if repo_name:
533
525
            try:
534
526
                repo_format = _mod_repository.network_format_registry.get(
535
527
                    repo_name)
536
528
            except KeyError:
537
529
                raise errors.UnknownFormatError(kind='repository',
538
 
                                                format=repo_name)
 
530
                    format=repo_name)
539
531
            format.repository_format = repo_format
540
532
        if branch_name:
541
533
            try:
543
535
                    branch.network_format_registry.get(branch_name))
544
536
            except KeyError:
545
537
                raise errors.UnknownFormatError(kind='branch',
546
 
                                                format=branch_name)
 
538
                    format=branch_name)
547
539
        return format
548
540
 
549
541
    def _vfs_cloning_metadir(self, require_stacking=False):
555
547
        medium = self._client._medium
556
548
        if medium._is_remote_before((1, 13)):
557
549
            return self._vfs_cloning_metadir(require_stacking=require_stacking)
558
 
        verb = b'BzrDir.cloning_metadir'
 
550
        verb = 'BzrDir.cloning_metadir'
559
551
        if require_stacking:
560
 
            stacking = b'True'
 
552
            stacking = 'True'
561
553
        else:
562
 
            stacking = b'False'
 
554
            stacking = 'False'
563
555
        path = self._path_for_remote_call(self._client)
564
556
        try:
565
557
            response = self._call(verb, path, stacking)
566
558
        except errors.UnknownSmartMethod:
567
559
            medium._remember_remote_is_before((1, 13))
568
560
            return self._vfs_cloning_metadir(require_stacking=require_stacking)
569
 
        except errors.UnknownErrorFromSmartServer as err:
570
 
            if err.error_tuple != (b'BranchReference',):
 
561
        except errors.UnknownErrorFromSmartServer, err:
 
562
            if err.error_tuple != ('BranchReference',):
571
563
                raise
572
564
            # We need to resolve the branch reference to determine the
573
565
            # cloning_metadir.  This causes unnecessary RPCs to open the
574
566
            # referenced branch (and bzrdir, etc) but only when the caller
575
567
            # didn't already resolve the branch reference.
576
568
            referenced_branch = self.open_branch()
577
 
            return referenced_branch.controldir.cloning_metadir()
 
569
            return referenced_branch.bzrdir.cloning_metadir()
578
570
        if len(response) != 3:
579
571
            raise errors.UnexpectedSmartServerResponse(response)
580
572
        control_name, repo_name, branch_info = response
584
576
        try:
585
577
            format = controldir.network_format_registry.get(control_name)
586
578
        except KeyError:
587
 
            raise errors.UnknownFormatError(
588
 
                kind='control', format=control_name)
 
579
            raise errors.UnknownFormatError(kind='control', format=control_name)
589
580
 
590
581
        if repo_name:
591
582
            try:
593
584
                    repo_name)
594
585
            except KeyError:
595
586
                raise errors.UnknownFormatError(kind='repository',
596
 
                                                format=repo_name)
597
 
        if branch_ref == b'ref':
 
587
                    format=repo_name)
 
588
        if branch_ref == 'ref':
598
589
            # XXX: we need possible_transports here to avoid reopening the
599
590
            # connection to the referenced location
600
591
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
601
592
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
602
593
            format.set_branch_format(branch_format)
603
 
        elif branch_ref == b'branch':
 
594
        elif branch_ref == 'branch':
604
595
            if branch_name:
605
596
                try:
606
597
                    branch_format = branch.network_format_registry.get(
607
598
                        branch_name)
608
599
                except KeyError:
609
600
                    raise errors.UnknownFormatError(kind='branch',
610
 
                                                    format=branch_name)
 
601
                        format=branch_name)
611
602
                format.set_branch_format(branch_format)
612
603
        else:
613
604
            raise errors.UnexpectedSmartServerResponse(response)
626
617
        """See BzrDir.destroy_repository"""
627
618
        path = self._path_for_remote_call(self._client)
628
619
        try:
629
 
            response = self._call(b'BzrDir.destroy_repository', path)
 
620
            response = self._call('BzrDir.destroy_repository', path)
630
621
        except errors.UnknownSmartMethod:
631
622
            self._ensure_real()
632
623
            self._real_bzrdir.destroy_repository()
633
624
            return
634
 
        if response[0] != b'ok':
635
 
            raise SmartProtocolError(
636
 
                'unexpected response code %s' % (response,))
 
625
        if response[0] != 'ok':
 
626
            raise SmartProtocolError('unexpected response code %s' % (response,))
637
627
 
638
628
    def create_branch(self, name=None, repository=None,
639
629
                      append_revisions_only=None):
644
634
        # as per meta1 formats - just delegate to the format object which may
645
635
        # be parameterised.
646
636
        real_branch = self._format.get_branch_format().initialize(self,
647
 
                                                                  name=name, repository=repository,
648
 
                                                                  append_revisions_only=append_revisions_only)
 
637
            name=name, repository=repository,
 
638
            append_revisions_only=append_revisions_only)
649
639
        if not isinstance(real_branch, RemoteBranch):
650
640
            if not isinstance(repository, RemoteRepository):
651
641
                raise AssertionError(
675
665
                args = (name, )
676
666
            else:
677
667
                args = ()
678
 
            response = self._call(b'BzrDir.destroy_branch', path, *args)
 
668
            response = self._call('BzrDir.destroy_branch', path, *args)
679
669
        except errors.UnknownSmartMethod:
680
670
            self._ensure_real()
681
671
            self._real_bzrdir.destroy_branch(name=name)
682
672
            self._next_open_branch_result = None
683
673
            return
684
674
        self._next_open_branch_result = None
685
 
        if response[0] != b'ok':
686
 
            raise SmartProtocolError(
687
 
                'unexpected response code %s' % (response,))
 
675
        if response[0] != 'ok':
 
676
            raise SmartProtocolError('unexpected response code %s' % (response,))
688
677
 
689
678
    def create_workingtree(self, revision_id=None, from_branch=None,
690
 
                           accelerator_tree=None, hardlink=False):
 
679
        accelerator_tree=None, hardlink=False):
691
680
        raise errors.NotLocalUrl(self.transport.base)
692
681
 
693
682
    def find_branch_format(self, name=None):
702
691
        path = self._path_for_remote_call(self._client)
703
692
        try:
704
693
            response, handler = self._call_expecting_body(
705
 
                b'BzrDir.get_branches', path)
 
694
                'BzrDir.get_branches', path)
706
695
        except errors.UnknownSmartMethod:
707
696
            self._ensure_real()
708
697
            return self._real_bzrdir.get_branches()
709
 
        if response[0] != b"success":
 
698
        if response[0] != "success":
710
699
            raise errors.UnexpectedSmartServerResponse(response)
711
700
        body = bencode.bdecode(handler.read_body_bytes())
712
701
        ret = {}
713
 
        for name, value in body.items():
714
 
            name = name.decode('utf-8')
 
702
        for (name, value) in body.iteritems():
715
703
            ret[name] = self._open_branch(name, value[0], value[1],
716
 
                                          possible_transports=possible_transports,
717
 
                                          ignore_fallbacks=ignore_fallbacks)
 
704
                possible_transports=possible_transports,
 
705
                ignore_fallbacks=ignore_fallbacks)
718
706
        return ret
719
707
 
720
708
    def set_branch_reference(self, target_branch, name=None):
734
722
            raise errors.NoColocatedBranchSupport(self)
735
723
        response = self._get_branch_reference()
736
724
        if response[0] == 'ref':
737
 
            return response[1].decode('utf-8')
 
725
            return response[1]
738
726
        else:
739
727
            return None
740
728
 
741
729
    def _get_branch_reference(self):
742
 
        """Get branch reference information
743
 
 
744
 
        :return: Tuple with (kind, location_or_format)
745
 
            if kind == 'ref', then location_or_format contains a location
746
 
            otherwise, it contains a format name
747
 
        """
748
730
        path = self._path_for_remote_call(self._client)
749
731
        medium = self._client._medium
750
732
        candidate_calls = [
751
 
            (b'BzrDir.open_branchV3', (2, 1)),
752
 
            (b'BzrDir.open_branchV2', (1, 13)),
753
 
            (b'BzrDir.open_branch', None),
 
733
            ('BzrDir.open_branchV3', (2, 1)),
 
734
            ('BzrDir.open_branchV2', (1, 13)),
 
735
            ('BzrDir.open_branch', None),
754
736
            ]
755
737
        for verb, required_version in candidate_calls:
756
738
            if required_version and medium._is_remote_before(required_version):
763
745
                medium._remember_remote_is_before(required_version)
764
746
            else:
765
747
                break
766
 
        if verb == b'BzrDir.open_branch':
767
 
            if response[0] != b'ok':
 
748
        if verb == 'BzrDir.open_branch':
 
749
            if response[0] != 'ok':
768
750
                raise errors.UnexpectedSmartServerResponse(response)
769
 
            if response[1] != b'':
 
751
            if response[1] != '':
770
752
                return ('ref', response[1])
771
753
            else:
772
 
                return ('branch', b'')
773
 
        if response[0] not in (b'ref', b'branch'):
 
754
                return ('branch', '')
 
755
        if response[0] not in ('ref', 'branch'):
774
756
            raise errors.UnexpectedSmartServerResponse(response)
775
 
        return (response[0].decode('ascii'), response[1])
 
757
        return response
776
758
 
777
759
    def _get_tree_branch(self, name=None):
778
760
        """See BzrDir._get_tree_branch()."""
784
766
            # a branch reference, use the existing BranchReference logic.
785
767
            format = BranchReferenceFormat()
786
768
            return format.open(self, name=name, _found=True,
787
 
                               location=location_or_format.decode('utf-8'),
788
 
                               ignore_fallbacks=ignore_fallbacks,
789
 
                               possible_transports=possible_transports)
 
769
                location=location_or_format, ignore_fallbacks=ignore_fallbacks,
 
770
                possible_transports=possible_transports)
790
771
        branch_format_name = location_or_format
791
772
        if not branch_format_name:
792
773
            branch_format_name = None
793
774
        format = RemoteBranchFormat(network_name=branch_format_name)
794
775
        return RemoteBranch(self, self.find_repository(), format=format,
795
 
                            setup_stacking=not ignore_fallbacks, name=name,
796
 
                            possible_transports=possible_transports)
 
776
            setup_stacking=not ignore_fallbacks, name=name,
 
777
            possible_transports=possible_transports)
797
778
 
798
779
    def open_branch(self, name=None, unsupported=False,
799
780
                    ignore_fallbacks=False, possible_transports=None):
802
783
        if name != "":
803
784
            raise errors.NoColocatedBranchSupport(self)
804
785
        if unsupported:
805
 
            raise NotImplementedError(
806
 
                'unsupported flag support not implemented yet.')
 
786
            raise NotImplementedError('unsupported flag support not implemented yet.')
807
787
        if self._next_open_branch_result is not None:
808
788
            # See create_branch for details.
809
789
            result = self._next_open_branch_result
811
791
            return result
812
792
        response = self._get_branch_reference()
813
793
        return self._open_branch(name, response[0], response[1],
814
 
                                 possible_transports=possible_transports,
815
 
                                 ignore_fallbacks=ignore_fallbacks)
 
794
            possible_transports=possible_transports,
 
795
            ignore_fallbacks=ignore_fallbacks)
816
796
 
817
797
    def _open_repo_v1(self, path):
818
 
        verb = b'BzrDir.find_repository'
 
798
        verb = 'BzrDir.find_repository'
819
799
        response = self._call(verb, path)
820
 
        if response[0] != b'ok':
 
800
        if response[0] != 'ok':
821
801
            raise errors.UnexpectedSmartServerResponse(response)
822
802
        # servers that only support the v1 method don't support external
823
803
        # references either.
824
804
        self._ensure_real()
825
805
        repo = self._real_bzrdir.open_repository()
826
 
        response = response + (b'no', repo._format.network_name())
 
806
        response = response + ('no', repo._format.network_name())
827
807
        return response, repo
828
808
 
829
809
    def _open_repo_v2(self, path):
830
 
        verb = b'BzrDir.find_repositoryV2'
 
810
        verb = 'BzrDir.find_repositoryV2'
831
811
        response = self._call(verb, path)
832
 
        if response[0] != b'ok':
 
812
        if response[0] != 'ok':
833
813
            raise errors.UnexpectedSmartServerResponse(response)
834
814
        self._ensure_real()
835
815
        repo = self._real_bzrdir.open_repository()
837
817
        return response, repo
838
818
 
839
819
    def _open_repo_v3(self, path):
840
 
        verb = b'BzrDir.find_repositoryV3'
 
820
        verb = 'BzrDir.find_repositoryV3'
841
821
        medium = self._client._medium
842
822
        if medium._is_remote_before((1, 13)):
843
823
            raise errors.UnknownSmartMethod(verb)
846
826
        except errors.UnknownSmartMethod:
847
827
            medium._remember_remote_is_before((1, 13))
848
828
            raise
849
 
        if response[0] != b'ok':
 
829
        if response[0] != 'ok':
850
830
            raise errors.UnexpectedSmartServerResponse(response)
851
831
        return response, None
852
832
 
854
834
        path = self._path_for_remote_call(self._client)
855
835
        response = None
856
836
        for probe in [self._open_repo_v3, self._open_repo_v2,
857
 
                      self._open_repo_v1]:
 
837
            self._open_repo_v1]:
858
838
            try:
859
839
                response, real_repo = probe(path)
860
840
                break
861
841
            except errors.UnknownSmartMethod:
862
842
                pass
863
843
        if response is None:
864
 
            raise errors.UnknownSmartMethod(b'BzrDir.find_repository{3,2,}')
865
 
        if response[0] != b'ok':
 
844
            raise errors.UnknownSmartMethod('BzrDir.find_repository{3,2,}')
 
845
        if response[0] != 'ok':
866
846
            raise errors.UnexpectedSmartServerResponse(response)
867
847
        if len(response) != 6:
868
 
            raise SmartProtocolError(
869
 
                'incorrect response length %s' % (response,))
870
 
        if response[1] == b'':
 
848
            raise SmartProtocolError('incorrect response length %s' % (response,))
 
849
        if response[1] == '':
871
850
            # repo is at this dir.
872
851
            format = response_tuple_to_repo_format(response[2:])
873
852
            # Used to support creating a real format instance when needed.
884
863
        if self._has_working_tree is None:
885
864
            path = self._path_for_remote_call(self._client)
886
865
            try:
887
 
                response = self._call(b'BzrDir.has_workingtree', path)
 
866
                response = self._call('BzrDir.has_workingtree', path)
888
867
            except errors.UnknownSmartMethod:
889
868
                self._ensure_real()
890
869
                self._has_working_tree = self._real_bzrdir.has_workingtree()
891
870
            else:
892
 
                if response[0] not in (b'yes', b'no'):
893
 
                    raise SmartProtocolError(
894
 
                        'unexpected response code %s' % (response,))
895
 
                self._has_working_tree = (response[0] == b'yes')
 
871
                if response[0] not in ('yes', 'no'):
 
872
                    raise SmartProtocolError('unexpected response code %s' % (response,))
 
873
                self._has_working_tree = (response[0] == 'yes')
896
874
        return self._has_working_tree
897
875
 
898
876
    def open_workingtree(self, recommend_upgrade=True):
903
881
 
904
882
    def _path_for_remote_call(self, client):
905
883
        """Return the path to be used for this bzrdir in a remote call."""
906
 
        remote_path = client.remote_path_from_transport(self.root_transport)
907
 
        remote_path = remote_path.decode('utf-8')
908
 
        base_url, segment_parameters = urlutils.split_segment_parameters_raw(
909
 
            remote_path)
910
 
        base_url = base_url.encode('utf-8')
911
 
        return base_url
 
884
        return urlutils.split_segment_parameters_raw(
 
885
            client.remote_path_from_transport(self.root_transport))[0]
912
886
 
913
887
    def get_branch_transport(self, branch_format, name=None):
914
888
        self._ensure_real()
937
911
        return RemoteControlStore(self)
938
912
 
939
913
 
940
 
class RemoteInventoryTree(InventoryRevisionTree):
941
 
 
942
 
    def __init__(self, repository, inv, revision_id):
943
 
        super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
944
 
 
945
 
    def archive(self, format, name, root=None, subdir=None, force_mtime=None):
946
 
        ret = self._repository._revision_archive(
947
 
            self.get_revision_id(), format, name, root, subdir,
948
 
            force_mtime=force_mtime)
949
 
        if ret is None:
950
 
            return super(RemoteInventoryTree, self).archive(
951
 
                format, name, root, subdir, force_mtime=force_mtime)
952
 
        return ret
953
 
 
954
 
    def annotate_iter(self, path,
955
 
                      default_revision=_mod_revision.CURRENT_REVISION):
956
 
        """Return an iterator of revision_id, line tuples.
957
 
 
958
 
        For working trees (and mutable trees in general), the special
959
 
        revision_id 'current:' will be used for lines that are new in this
960
 
        tree, e.g. uncommitted changes.
961
 
        :param default_revision: For lines that don't match a basis, mark them
962
 
            with this revision id. Not all implementations will make use of
963
 
            this value.
964
 
        """
965
 
        ret = self._repository._annotate_file_revision(
966
 
            self.get_revision_id(), path, file_id=None,
967
 
            default_revision=default_revision)
968
 
        if ret is None:
969
 
            return super(RemoteInventoryTree, self).annotate_iter(
970
 
                path, default_revision=default_revision)
971
 
        return ret
972
 
 
973
 
 
974
914
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
975
915
    """Format for repositories accessed over a _SmartClient.
976
916
 
991
931
        to obtain data like the network name.
992
932
    """
993
933
 
994
 
    _matchingcontroldir = RemoteBzrDirFormat()
 
934
    _matchingbzrdir = RemoteBzrDirFormat()
995
935
    supports_full_versioned_files = True
996
936
    supports_leaving_lock = True
997
 
    supports_overriding_transport = False
998
937
 
999
938
    def __init__(self):
1000
939
        _mod_repository.RepositoryFormat.__init__(self)
1011
950
 
1012
951
    def __repr__(self):
1013
952
        return "%s(_network_name=%r)" % (self.__class__.__name__,
1014
 
                                         self._network_name)
 
953
            self._network_name)
1015
954
 
1016
955
    @property
1017
956
    def fast_deltas(self):
1072
1011
                self._custom_format.revision_graph_can_have_wrong_parents
1073
1012
        return self._revision_graph_can_have_wrong_parents
1074
1013
 
1075
 
    def _vfs_initialize(self, a_controldir, shared):
 
1014
    def _vfs_initialize(self, a_bzrdir, shared):
1076
1015
        """Helper for common code in initialize."""
1077
1016
        if self._custom_format:
1078
1017
            # Custom format requested
1079
 
            result = self._custom_format.initialize(
1080
 
                a_controldir, shared=shared)
 
1018
            result = self._custom_format.initialize(a_bzrdir, shared=shared)
1081
1019
        elif self._creating_bzrdir is not None:
1082
1020
            # Use the format that the repository we were created to back
1083
1021
            # has.
1084
1022
            prior_repo = self._creating_bzrdir.open_repository()
1085
1023
            prior_repo._ensure_real()
1086
1024
            result = prior_repo._real_repository._format.initialize(
1087
 
                a_controldir, shared=shared)
 
1025
                a_bzrdir, shared=shared)
1088
1026
        else:
1089
1027
            # assume that a_bzr is a RemoteBzrDir but the smart server didn't
1090
1028
            # support remote initialization.
1091
1029
            # We delegate to a real object at this point (as RemoteBzrDir
1092
1030
            # delegate to the repository format which would lead to infinite
1093
 
            # recursion if we just called a_controldir.create_repository.
1094
 
            a_controldir._ensure_real()
1095
 
            result = a_controldir._real_bzrdir.create_repository(shared=shared)
 
1031
            # recursion if we just called a_bzrdir.create_repository.
 
1032
            a_bzrdir._ensure_real()
 
1033
            result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1096
1034
        if not isinstance(result, RemoteRepository):
1097
 
            return self.open(a_controldir)
 
1035
            return self.open(a_bzrdir)
1098
1036
        else:
1099
1037
            return result
1100
1038
 
1101
 
    def initialize(self, a_controldir, shared=False):
 
1039
    def initialize(self, a_bzrdir, shared=False):
1102
1040
        # Being asked to create on a non RemoteBzrDir:
1103
 
        if not isinstance(a_controldir, RemoteBzrDir):
1104
 
            return self._vfs_initialize(a_controldir, shared)
1105
 
        medium = a_controldir._client._medium
 
1041
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
1042
            return self._vfs_initialize(a_bzrdir, shared)
 
1043
        medium = a_bzrdir._client._medium
1106
1044
        if medium._is_remote_before((1, 13)):
1107
 
            return self._vfs_initialize(a_controldir, shared)
 
1045
            return self._vfs_initialize(a_bzrdir, shared)
1108
1046
        # Creating on a remote bzr dir.
1109
1047
        # 1) get the network name to use.
1110
1048
        if self._custom_format:
1112
1050
        elif self._network_name:
1113
1051
            network_name = self._network_name
1114
1052
        else:
1115
 
            # Select the current breezy default and ask for that.
1116
 
            reference_bzrdir_format = controldir.format_registry.get(
1117
 
                'default')()
 
1053
            # Select the current brzlib default and ask for that.
 
1054
            reference_bzrdir_format = controldir.format_registry.get('default')()
1118
1055
            reference_format = reference_bzrdir_format.repository_format
1119
1056
            network_name = reference_format.network_name()
1120
1057
        # 2) try direct creation via RPC
1121
 
        path = a_controldir._path_for_remote_call(a_controldir._client)
1122
 
        verb = b'BzrDir.create_repository'
 
1058
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
1059
        verb = 'BzrDir.create_repository'
1123
1060
        if shared:
1124
 
            shared_str = b'True'
 
1061
            shared_str = 'True'
1125
1062
        else:
1126
 
            shared_str = b'False'
 
1063
            shared_str = 'False'
1127
1064
        try:
1128
 
            response = a_controldir._call(verb, path, network_name, shared_str)
 
1065
            response = a_bzrdir._call(verb, path, network_name, shared_str)
1129
1066
        except errors.UnknownSmartMethod:
1130
1067
            # Fallback - use vfs methods
1131
1068
            medium._remember_remote_is_before((1, 13))
1132
 
            return self._vfs_initialize(a_controldir, shared)
 
1069
            return self._vfs_initialize(a_bzrdir, shared)
1133
1070
        else:
1134
1071
            # Turn the response into a RemoteRepository object.
1135
1072
            format = response_tuple_to_repo_format(response[1:])
1136
1073
            # Used to support creating a real format instance when needed.
1137
 
            format._creating_bzrdir = a_controldir
1138
 
            remote_repo = RemoteRepository(a_controldir, format)
 
1074
            format._creating_bzrdir = a_bzrdir
 
1075
            remote_repo = RemoteRepository(a_bzrdir, format)
1139
1076
            format._creating_repo = remote_repo
1140
1077
            return remote_repo
1141
1078
 
1142
 
    def open(self, a_controldir):
1143
 
        if not isinstance(a_controldir, RemoteBzrDir):
1144
 
            raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1145
 
        return a_controldir.open_repository()
 
1079
    def open(self, a_bzrdir):
 
1080
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
1081
            raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
 
1082
        return a_bzrdir.open_repository()
1146
1083
 
1147
1084
    def _ensure_real(self):
1148
1085
        if self._custom_format is None:
1151
1088
                    self._network_name)
1152
1089
            except KeyError:
1153
1090
                raise errors.UnknownFormatError(kind='repository',
1154
 
                                                format=self._network_name)
 
1091
                    format=self._network_name)
1155
1092
 
1156
1093
    @property
1157
1094
    def _fetch_order(self):
1193
1130
 
1194
1131
 
1195
1132
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
1196
 
                       lock._RelockDebugMixin):
 
1133
        lock._RelockDebugMixin):
1197
1134
    """Repository accessed over rpc.
1198
1135
 
1199
1136
    For the moment most operations are performed using local transport-backed
1215
1152
            self._real_repository = real_repository
1216
1153
        else:
1217
1154
            self._real_repository = None
1218
 
        self.controldir = remote_bzrdir
 
1155
        self.bzrdir = remote_bzrdir
1219
1156
        if _client is None:
1220
1157
            self._client = remote_bzrdir._client
1221
1158
        else:
1239
1176
        self._reconcile_does_inventory_gc = False
1240
1177
        self._reconcile_fixes_text_parents = False
1241
1178
        self._reconcile_backsup_inventory = False
1242
 
        self.base = self.controldir.transport.base
 
1179
        self.base = self.bzrdir.transport.base
1243
1180
        # Additional places to query for data.
1244
1181
        self._fallback_repositories = []
1245
1182
 
1246
1183
    @property
1247
1184
    def user_transport(self):
1248
 
        return self.controldir.user_transport
 
1185
        return self.bzrdir.user_transport
1249
1186
 
1250
1187
    @property
1251
1188
    def control_transport(self):
1252
1189
        # XXX: Normally you shouldn't directly get at the remote repository
1253
1190
        # transport, but I'm not sure it's worth making this method
1254
1191
        # optional -- mbp 2010-04-21
1255
 
        return self.controldir.get_repository_transport(None)
 
1192
        return self.bzrdir.get_repository_transport(None)
1256
1193
 
1257
1194
    def __str__(self):
1258
1195
        return "%s(%s)" % (self.__class__.__name__, self.base)
1278
1215
                mutter('(suppressed) not in write group')
1279
1216
                return
1280
1217
            raise errors.BzrError("not in write group")
1281
 
        path = self.controldir._path_for_remote_call(self._client)
 
1218
        path = self.bzrdir._path_for_remote_call(self._client)
1282
1219
        try:
1283
 
            response = self._call(b'Repository.abort_write_group', path,
1284
 
                                  self._lock_token,
1285
 
                                  [token.encode('utf-8') for token in self._write_group_tokens])
1286
 
        except Exception as exc:
 
1220
            response = self._call('Repository.abort_write_group', path,
 
1221
                self._lock_token, self._write_group_tokens)
 
1222
        except Exception, exc:
1287
1223
            self._write_group = None
1288
1224
            if not suppress_errors:
1289
1225
                raise
1291
1227
            log_exception_quietly()
1292
1228
            note(gettext('bzr: ERROR (ignored): %s'), exc)
1293
1229
        else:
1294
 
            if response != (b'ok', ):
 
1230
            if response != ('ok', ):
1295
1231
                raise errors.UnexpectedSmartServerResponse(response)
1296
1232
            self._write_group_tokens = None
1297
1233
 
1318
1254
            return self._real_repository.commit_write_group()
1319
1255
        if not self.is_in_write_group():
1320
1256
            raise errors.BzrError("not in write group")
1321
 
        path = self.controldir._path_for_remote_call(self._client)
1322
 
        response = self._call(b'Repository.commit_write_group', path,
1323
 
                              self._lock_token, [token.encode('utf-8') for token in self._write_group_tokens])
1324
 
        if response != (b'ok', ):
 
1257
        path = self.bzrdir._path_for_remote_call(self._client)
 
1258
        response = self._call('Repository.commit_write_group', path,
 
1259
            self._lock_token, self._write_group_tokens)
 
1260
        if response != ('ok', ):
1325
1261
            raise errors.UnexpectedSmartServerResponse(response)
1326
1262
        self._write_group_tokens = None
1327
1263
        # Refresh data after writing to the repository.
1330
1266
    def resume_write_group(self, tokens):
1331
1267
        if self._real_repository:
1332
1268
            return self._real_repository.resume_write_group(tokens)
1333
 
        path = self.controldir._path_for_remote_call(self._client)
 
1269
        path = self.bzrdir._path_for_remote_call(self._client)
1334
1270
        try:
1335
 
            response = self._call(b'Repository.check_write_group', path,
1336
 
                                  self._lock_token, [token.encode('utf-8') for token in tokens])
 
1271
            response = self._call('Repository.check_write_group', path,
 
1272
               self._lock_token, tokens)
1337
1273
        except errors.UnknownSmartMethod:
1338
1274
            self._ensure_real()
1339
1275
            return self._real_repository.resume_write_group(tokens)
1340
 
        if response != (b'ok', ):
 
1276
        if response != ('ok', ):
1341
1277
            raise errors.UnexpectedSmartServerResponse(response)
1342
1278
        self._write_group_tokens = tokens
1343
1279
 
1360
1296
 
1361
1297
    def get_rev_id_for_revno(self, revno, known_pair):
1362
1298
        """See Repository.get_rev_id_for_revno."""
1363
 
        path = self.controldir._path_for_remote_call(self._client)
 
1299
        path = self.bzrdir._path_for_remote_call(self._client)
1364
1300
        try:
1365
1301
            if self._client._medium._is_remote_before((1, 17)):
1366
1302
                return self._get_rev_id_for_revno_vfs(revno, known_pair)
1367
1303
            response = self._call(
1368
 
                b'Repository.get_rev_id_for_revno', path, revno, known_pair)
 
1304
                'Repository.get_rev_id_for_revno', path, revno, known_pair)
1369
1305
        except errors.UnknownSmartMethod:
1370
1306
            self._client._medium._remember_remote_is_before((1, 17))
1371
1307
            return self._get_rev_id_for_revno_vfs(revno, known_pair)
1372
 
        except errors.UnknownErrorFromSmartServer as e:
1373
 
            # Older versions of Bazaar/Breezy (<< 3.0.0) would raise a
1374
 
            # ValueError instead of returning revno-outofbounds
1375
 
            if len(e.error_tuple) < 3:
1376
 
                raise
1377
 
            if e.error_tuple[:2] != (b'error', b'ValueError'):
1378
 
                raise
1379
 
            m = re.match(
1380
 
                br"requested revno \(([0-9]+)\) is later than given "
1381
 
                br"known revno \(([0-9]+)\)", e.error_tuple[2])
1382
 
            if not m:
1383
 
                raise
1384
 
            raise errors.RevnoOutOfBounds(
1385
 
                int(m.group(1)), (0, int(m.group(2))))
1386
 
        if response[0] == b'ok':
 
1308
        if response[0] == 'ok':
1387
1309
            return True, response[1]
1388
 
        elif response[0] == b'history-incomplete':
 
1310
        elif response[0] == 'history-incomplete':
1389
1311
            known_pair = response[1:3]
1390
1312
            for fallback in self._fallback_repositories:
1391
 
                found, result = fallback.get_rev_id_for_revno(
1392
 
                    revno, known_pair)
 
1313
                found, result = fallback.get_rev_id_for_revno(revno, known_pair)
1393
1314
                if found:
1394
1315
                    return True, result
1395
1316
                else:
1416
1337
            if 'hpssvfs' in debug.debug_flags:
1417
1338
                import traceback
1418
1339
                warning('VFS Repository access triggered\n%s',
1419
 
                        ''.join(traceback.format_stack()))
 
1340
                    ''.join(traceback.format_stack()))
1420
1341
            self._unstacked_provider.missing_keys.clear()
1421
 
            self.controldir._ensure_real()
 
1342
            self.bzrdir._ensure_real()
1422
1343
            self._set_real_repository(
1423
 
                self.controldir._real_bzrdir.open_repository())
 
1344
                self.bzrdir._real_bzrdir.open_repository())
1424
1345
 
1425
1346
    def _translate_error(self, err, **context):
1426
 
        self.controldir._translate_error(err, repository=self, **context)
 
1347
        self.bzrdir._translate_error(err, repository=self, **context)
1427
1348
 
1428
1349
    def find_text_key_references(self):
1429
1350
        """Find the text key references within the repository.
1450
1371
    def _get_revision_graph(self, revision_id):
1451
1372
        """Private method for using with old (< 1.2) servers to fallback."""
1452
1373
        if revision_id is None:
1453
 
            revision_id = b''
 
1374
            revision_id = ''
1454
1375
        elif _mod_revision.is_null(revision_id):
1455
1376
            return {}
1456
1377
 
1457
 
        path = self.controldir._path_for_remote_call(self._client)
 
1378
        path = self.bzrdir._path_for_remote_call(self._client)
1458
1379
        response = self._call_expecting_body(
1459
 
            b'Repository.get_revision_graph', path, revision_id)
 
1380
            'Repository.get_revision_graph', path, revision_id)
1460
1381
        response_tuple, response_handler = response
1461
 
        if response_tuple[0] != b'ok':
 
1382
        if response_tuple[0] != 'ok':
1462
1383
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1463
1384
        coded = response_handler.read_body_bytes()
1464
 
        if coded == b'':
 
1385
        if coded == '':
1465
1386
            # no revisions in this repository!
1466
1387
            return {}
1467
 
        lines = coded.split(b'\n')
 
1388
        lines = coded.split('\n')
1468
1389
        revision_graph = {}
1469
1390
        for line in lines:
1470
1391
            d = tuple(line.split())
1480
1401
        """Return a source for streaming from this repository."""
1481
1402
        return RemoteStreamSource(self, to_format)
1482
1403
 
 
1404
    @needs_read_lock
1483
1405
    def get_file_graph(self):
1484
 
        with self.lock_read():
1485
 
            return graph.Graph(self.texts)
 
1406
        return graph.Graph(self.texts)
1486
1407
 
 
1408
    @needs_read_lock
1487
1409
    def has_revision(self, revision_id):
1488
1410
        """True if this repository has a copy of the revision."""
1489
 
        # Copy of breezy.repository.Repository.has_revision
1490
 
        with self.lock_read():
1491
 
            return revision_id in self.has_revisions((revision_id,))
 
1411
        # Copy of brzlib.repository.Repository.has_revision
 
1412
        return revision_id in self.has_revisions((revision_id,))
1492
1413
 
 
1414
    @needs_read_lock
1493
1415
    def has_revisions(self, revision_ids):
1494
1416
        """Probe to find out the presence of multiple revisions.
1495
1417
 
1496
1418
        :param revision_ids: An iterable of revision_ids.
1497
1419
        :return: A set of the revision_ids that were present.
1498
1420
        """
1499
 
        with self.lock_read():
1500
 
            # Copy of breezy.repository.Repository.has_revisions
1501
 
            parent_map = self.get_parent_map(revision_ids)
1502
 
            result = set(parent_map)
1503
 
            if _mod_revision.NULL_REVISION in revision_ids:
1504
 
                result.add(_mod_revision.NULL_REVISION)
1505
 
            return result
 
1421
        # Copy of brzlib.repository.Repository.has_revisions
 
1422
        parent_map = self.get_parent_map(revision_ids)
 
1423
        result = set(parent_map)
 
1424
        if _mod_revision.NULL_REVISION in revision_ids:
 
1425
            result.add(_mod_revision.NULL_REVISION)
 
1426
        return result
1506
1427
 
1507
1428
    def _has_same_fallbacks(self, other_repo):
1508
1429
        """Returns true if the repositories have the same fallbacks."""
1521
1442
        # TODO: Move to RepositoryBase and unify with the regular Repository
1522
1443
        # one; unfortunately the tests rely on slightly different behaviour at
1523
1444
        # present -- mbp 20090710
1524
 
        return (self.__class__ is other.__class__
1525
 
                and self.controldir.transport.base == other.controldir.transport.base)
 
1445
        return (self.__class__ is other.__class__ and
 
1446
                self.bzrdir.transport.base == other.bzrdir.transport.base)
1526
1447
 
1527
1448
    def get_graph(self, other_repository=None):
1528
1449
        """Return the graph for this repository format"""
1529
1450
        parents_provider = self._make_parents_provider(other_repository)
1530
1451
        return graph.Graph(parents_provider)
1531
1452
 
 
1453
    @needs_read_lock
1532
1454
    def get_known_graph_ancestry(self, revision_ids):
1533
1455
        """Return the known graph for a set of revision ids and their ancestors.
1534
1456
        """
1535
 
        with self.lock_read():
1536
 
            revision_graph = dict(((key, value) for key, value in
1537
 
                                   self.get_graph().iter_ancestry(revision_ids) if value is not None))
1538
 
            revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1539
 
            return graph.KnownGraph(revision_graph)
 
1457
        st = static_tuple.StaticTuple
 
1458
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
 
1459
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
 
1460
        return graph.GraphThunkIdsToKeys(known_graph)
1540
1461
 
1541
1462
    def gather_stats(self, revid=None, committers=None):
1542
1463
        """See Repository.gather_stats()."""
1543
 
        path = self.controldir._path_for_remote_call(self._client)
 
1464
        path = self.bzrdir._path_for_remote_call(self._client)
1544
1465
        # revid can be None to indicate no revisions, not just NULL_REVISION
1545
1466
        if revid is None or _mod_revision.is_null(revid):
1546
 
            fmt_revid = b''
 
1467
            fmt_revid = ''
1547
1468
        else:
1548
1469
            fmt_revid = revid
1549
1470
        if committers is None or not committers:
1550
 
            fmt_committers = b'no'
 
1471
            fmt_committers = 'no'
1551
1472
        else:
1552
 
            fmt_committers = b'yes'
 
1473
            fmt_committers = 'yes'
1553
1474
        response_tuple, response_handler = self._call_expecting_body(
1554
 
            b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1555
 
        if response_tuple[0] != b'ok':
 
1475
            'Repository.gather_stats', path, fmt_revid, fmt_committers)
 
1476
        if response_tuple[0] != 'ok':
1556
1477
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1557
1478
 
1558
1479
        body = response_handler.read_body_bytes()
1559
1480
        result = {}
1560
 
        for line in body.split(b'\n'):
 
1481
        for line in body.split('\n'):
1561
1482
            if not line:
1562
1483
                continue
1563
 
            key, val_text = line.split(b':')
1564
 
            key = key.decode('ascii')
 
1484
            key, val_text = line.split(':')
1565
1485
            if key in ('revisions', 'size', 'committers'):
1566
1486
                result[key] = int(val_text)
1567
1487
            elif key in ('firstrev', 'latestrev'):
1568
 
                values = val_text.split(b' ')[1:]
1569
 
                result[key] = (float(values[0]), int(values[1]))
 
1488
                values = val_text.split(' ')[1:]
 
1489
                result[key] = (float(values[0]), long(values[1]))
1570
1490
 
1571
1491
        return result
1572
1492
 
1578
1498
 
1579
1499
    def get_physical_lock_status(self):
1580
1500
        """See Repository.get_physical_lock_status()."""
1581
 
        path = self.controldir._path_for_remote_call(self._client)
 
1501
        path = self.bzrdir._path_for_remote_call(self._client)
1582
1502
        try:
1583
 
            response = self._call(b'Repository.get_physical_lock_status', path)
 
1503
            response = self._call('Repository.get_physical_lock_status', path)
1584
1504
        except errors.UnknownSmartMethod:
1585
1505
            self._ensure_real()
1586
1506
            return self._real_repository.get_physical_lock_status()
1587
 
        if response[0] not in (b'yes', b'no'):
 
1507
        if response[0] not in ('yes', 'no'):
1588
1508
            raise errors.UnexpectedSmartServerResponse(response)
1589
 
        return (response[0] == b'yes')
 
1509
        return (response[0] == 'yes')
1590
1510
 
1591
1511
    def is_in_write_group(self):
1592
1512
        """Return True if there is an open write group.
1603
1523
 
1604
1524
    def is_shared(self):
1605
1525
        """See Repository.is_shared()."""
1606
 
        path = self.controldir._path_for_remote_call(self._client)
1607
 
        response = self._call(b'Repository.is_shared', path)
1608
 
        if response[0] not in (b'yes', b'no'):
1609
 
            raise SmartProtocolError(
1610
 
                'unexpected response code %s' % (response,))
1611
 
        return response[0] == b'yes'
 
1526
        path = self.bzrdir._path_for_remote_call(self._client)
 
1527
        response = self._call('Repository.is_shared', path)
 
1528
        if response[0] not in ('yes', 'no'):
 
1529
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
1530
        return response[0] == 'yes'
1612
1531
 
1613
1532
    def is_write_locked(self):
1614
1533
        return self._lock_mode == 'w'
1621
1540
    def lock_read(self):
1622
1541
        """Lock the repository for read operations.
1623
1542
 
1624
 
        :return: A breezy.lock.LogicalLockResult.
 
1543
        :return: A brzlib.lock.LogicalLockResult.
1625
1544
        """
1626
1545
        # wrong eventually - want a local lock cache context
1627
1546
        if not self._lock_mode:
1638
1557
        return lock.LogicalLockResult(self.unlock)
1639
1558
 
1640
1559
    def _remote_lock_write(self, token):
1641
 
        path = self.controldir._path_for_remote_call(self._client)
 
1560
        path = self.bzrdir._path_for_remote_call(self._client)
1642
1561
        if token is None:
1643
 
            token = b''
 
1562
            token = ''
1644
1563
        err_context = {'token': token}
1645
 
        response = self._call(b'Repository.lock_write', path, token,
 
1564
        response = self._call('Repository.lock_write', path, token,
1646
1565
                              **err_context)
1647
 
        if response[0] == b'ok':
 
1566
        if response[0] == 'ok':
1648
1567
            ok, token = response
1649
1568
            return token
1650
1569
        else:
1722
1641
        # 3) new servers, RemoteRepository.ensure_real is triggered before
1723
1642
        # RemoteBranch.ensure real, in this case we get a repo with no fallbacks
1724
1643
        # and need to populate it.
1725
 
        if (self._fallback_repositories
1726
 
            and len(self._real_repository._fallback_repositories)
1727
 
                != len(self._fallback_repositories)):
 
1644
        if (self._fallback_repositories and
 
1645
            len(self._real_repository._fallback_repositories) !=
 
1646
            len(self._fallback_repositories)):
1728
1647
            if len(self._real_repository._fallback_repositories):
1729
1648
                raise AssertionError(
1730
1649
                    "cannot cleanly remove existing _fallback_repositories")
1756
1675
            raise errors.NotWriteLocked(self)
1757
1676
        if self._write_group_tokens is not None:
1758
1677
            raise errors.BzrError('already in a write group')
1759
 
        path = self.controldir._path_for_remote_call(self._client)
 
1678
        path = self.bzrdir._path_for_remote_call(self._client)
1760
1679
        try:
1761
 
            response = self._call(b'Repository.start_write_group', path,
1762
 
                                  self._lock_token)
 
1680
            response = self._call('Repository.start_write_group', path,
 
1681
                self._lock_token)
1763
1682
        except (errors.UnknownSmartMethod, errors.UnsuspendableWriteGroup):
1764
1683
            self._ensure_real()
1765
1684
            return self._real_repository.start_write_group()
1766
 
        if response[0] != b'ok':
 
1685
        if response[0] != 'ok':
1767
1686
            raise errors.UnexpectedSmartServerResponse(response)
1768
 
        self._write_group_tokens = [
1769
 
            token.decode('utf-8') for token in response[1]]
 
1687
        self._write_group_tokens = response[1]
1770
1688
 
1771
1689
    def _unlock(self, token):
1772
 
        path = self.controldir._path_for_remote_call(self._client)
 
1690
        path = self.bzrdir._path_for_remote_call(self._client)
1773
1691
        if not token:
1774
1692
            # with no token the remote repository is not persistently locked.
1775
1693
            return
1776
1694
        err_context = {'token': token}
1777
 
        response = self._call(b'Repository.unlock', path, token,
 
1695
        response = self._call('Repository.unlock', path, token,
1778
1696
                              **err_context)
1779
 
        if response == (b'ok',):
 
1697
        if response == ('ok',):
1780
1698
            return
1781
1699
        else:
1782
1700
            raise errors.UnexpectedSmartServerResponse(response)
1818
1736
 
1819
1737
    def break_lock(self):
1820
1738
        # should hand off to the network
1821
 
        path = self.controldir._path_for_remote_call(self._client)
 
1739
        path = self.bzrdir._path_for_remote_call(self._client)
1822
1740
        try:
1823
 
            response = self._call(b"Repository.break_lock", path)
 
1741
            response = self._call("Repository.break_lock", path)
1824
1742
        except errors.UnknownSmartMethod:
1825
1743
            self._ensure_real()
1826
1744
            return self._real_repository.break_lock()
1827
 
        if response != (b'ok',):
 
1745
        if response != ('ok',):
1828
1746
            raise errors.UnexpectedSmartServerResponse(response)
1829
1747
 
1830
1748
    def _get_tarball(self, compression):
1833
1751
        Returns None if the server does not support sending tarballs.
1834
1752
        """
1835
1753
        import tempfile
1836
 
        path = self.controldir._path_for_remote_call(self._client)
 
1754
        path = self.bzrdir._path_for_remote_call(self._client)
1837
1755
        try:
1838
1756
            response, protocol = self._call_expecting_body(
1839
 
                b'Repository.tarball', path, compression.encode('ascii'))
 
1757
                'Repository.tarball', path, compression)
1840
1758
        except errors.UnknownSmartMethod:
1841
1759
            protocol.cancel_read_body()
1842
1760
            return None
1843
 
        if response[0] == b'ok':
 
1761
        if response[0] == 'ok':
1844
1762
            # Extract the tarball and return it
1845
1763
            t = tempfile.NamedTemporaryFile()
1846
1764
            # TODO: rpc layer should read directly into it...
1849
1767
            return t
1850
1768
        raise errors.UnexpectedSmartServerResponse(response)
1851
1769
 
 
1770
    @needs_read_lock
1852
1771
    def sprout(self, to_bzrdir, revision_id=None):
1853
1772
        """Create a descendent repository for new development.
1854
1773
 
1855
1774
        Unlike clone, this does not copy the settings of the repository.
1856
1775
        """
1857
 
        with self.lock_read():
1858
 
            dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1859
 
            dest_repo.fetch(self, revision_id=revision_id)
1860
 
            return dest_repo
 
1776
        dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
 
1777
        dest_repo.fetch(self, revision_id=revision_id)
 
1778
        return dest_repo
1861
1779
 
1862
 
    def _create_sprouting_repo(self, a_controldir, shared):
1863
 
        if not isinstance(a_controldir._format, self.controldir._format.__class__):
 
1780
    def _create_sprouting_repo(self, a_bzrdir, shared):
 
1781
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
1864
1782
            # use target default format.
1865
 
            dest_repo = a_controldir.create_repository()
 
1783
            dest_repo = a_bzrdir.create_repository()
1866
1784
        else:
1867
1785
            # Most control formats need the repository to be specifically
1868
1786
            # created, but on some old all-in-one formats it's not needed
1869
1787
            try:
1870
 
                dest_repo = self._format.initialize(
1871
 
                    a_controldir, shared=shared)
 
1788
                dest_repo = self._format.initialize(a_bzrdir, shared=shared)
1872
1789
            except errors.UninitializableFormat:
1873
 
                dest_repo = a_controldir.open_repository()
 
1790
                dest_repo = a_bzrdir.open_repository()
1874
1791
        return dest_repo
1875
1792
 
1876
 
    # These methods are just thin shims to the VFS object for now.
 
1793
    ### These methods are just thin shims to the VFS object for now.
1877
1794
 
 
1795
    @needs_read_lock
1878
1796
    def revision_tree(self, revision_id):
1879
 
        with self.lock_read():
1880
 
            revision_id = _mod_revision.ensure_null(revision_id)
1881
 
            if revision_id == _mod_revision.NULL_REVISION:
1882
 
                return InventoryRevisionTree(self,
1883
 
                                             Inventory(root_id=None), _mod_revision.NULL_REVISION)
1884
 
            else:
1885
 
                return list(self.revision_trees([revision_id]))[0]
 
1797
        revision_id = _mod_revision.ensure_null(revision_id)
 
1798
        if revision_id == _mod_revision.NULL_REVISION:
 
1799
            return InventoryRevisionTree(self,
 
1800
                Inventory(root_id=None), _mod_revision.NULL_REVISION)
 
1801
        else:
 
1802
            return list(self.revision_trees([revision_id]))[0]
1886
1803
 
1887
1804
    def get_serializer_format(self):
1888
 
        path = self.controldir._path_for_remote_call(self._client)
 
1805
        path = self.bzrdir._path_for_remote_call(self._client)
1889
1806
        try:
1890
 
            response = self._call(b'VersionedFileRepository.get_serializer_format',
1891
 
                                  path)
 
1807
            response = self._call('VersionedFileRepository.get_serializer_format',
 
1808
                path)
1892
1809
        except errors.UnknownSmartMethod:
1893
1810
            self._ensure_real()
1894
1811
            return self._real_repository.get_serializer_format()
1895
 
        if response[0] != b'ok':
 
1812
        if response[0] != 'ok':
1896
1813
            raise errors.UnexpectedSmartServerResponse(response)
1897
1814
        return response[1]
1898
1815
 
1914
1831
        """
1915
1832
        if self._fallback_repositories and not self._format.supports_chks:
1916
1833
            raise errors.BzrError("Cannot commit directly to a stacked branch"
1917
 
                                  " in pre-2a formats. See "
1918
 
                                  "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1919
 
        commit_builder_kls = vf_repository.VersionedFileCommitBuilder
 
1834
                " in pre-2a formats. See "
 
1835
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
 
1836
        if self._format.rich_root_data:
 
1837
            commit_builder_kls = vf_repository.VersionedFileRootCommitBuilder
 
1838
        else:
 
1839
            commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1920
1840
        result = commit_builder_kls(self, parents, config,
1921
 
                                    timestamp, timezone, committer, revprops, revision_id,
1922
 
                                    lossy)
 
1841
            timestamp, timezone, committer, revprops, revision_id,
 
1842
            lossy)
1923
1843
        self.start_write_group()
1924
1844
        return result
1925
1845
 
1947
1867
        # repository to be added may already be in the _real_repositories list.
1948
1868
        if self._real_repository is not None:
1949
1869
            fallback_locations = [repo.user_url for repo in
1950
 
                                  self._real_repository._fallback_repositories]
 
1870
                self._real_repository._fallback_repositories]
1951
1871
            if repository.user_url not in fallback_locations:
1952
1872
                self._real_repository.add_fallback_repository(repository)
1953
1873
 
1966
1886
        return self._real_repository.add_inventory(revid, inv, parents)
1967
1887
 
1968
1888
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1969
 
                               parents, basis_inv=None, propagate_caches=False):
 
1889
            parents, basis_inv=None, propagate_caches=False):
1970
1890
        self._ensure_real()
1971
1891
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
1972
 
                                                            delta, new_revision_id, parents, basis_inv=basis_inv,
1973
 
                                                            propagate_caches=propagate_caches)
 
1892
            delta, new_revision_id, parents, basis_inv=basis_inv,
 
1893
            propagate_caches=propagate_caches)
1974
1894
 
1975
1895
    def add_revision(self, revision_id, rev, inv=None):
1976
1896
        _mod_revision.check_not_reserved_id(revision_id)
1991
1911
    def _add_revision(self, rev):
1992
1912
        if self._real_repository is not None:
1993
1913
            return self._real_repository._add_revision(rev)
1994
 
        lines = self._serializer.write_revision_to_lines(rev)
 
1914
        text = self._serializer.write_revision_to_string(rev)
1995
1915
        key = (rev.revision_id,)
1996
1916
        parents = tuple((parent,) for parent in rev.parent_ids)
1997
1917
        self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1998
 
            [('revisions', [ChunkedContentFactory(key, parents, None, lines, chunks_are_lines=True)])],
 
1918
            [('revisions', [FulltextContentFactory(key, parents, None, text)])],
1999
1919
            self._format, self._write_group_tokens)
2000
1920
 
 
1921
    @needs_read_lock
2001
1922
    def get_inventory(self, revision_id):
2002
 
        with self.lock_read():
2003
 
            return list(self.iter_inventories([revision_id]))[0]
 
1923
        return list(self.iter_inventories([revision_id]))[0]
2004
1924
 
2005
1925
    def _iter_inventories_rpc(self, revision_ids, ordering):
2006
1926
        if ordering is None:
2007
1927
            ordering = 'unordered'
2008
 
        path = self.controldir._path_for_remote_call(self._client)
2009
 
        body = b"\n".join(revision_ids)
 
1928
        path = self.bzrdir._path_for_remote_call(self._client)
 
1929
        body = "\n".join(revision_ids)
2010
1930
        response_tuple, response_handler = (
2011
1931
            self._call_with_body_bytes_expecting_body(
2012
 
                b"VersionedFileRepository.get_inventories",
2013
 
                (path, ordering.encode('ascii')), body))
2014
 
        if response_tuple[0] != b"ok":
 
1932
                "VersionedFileRepository.get_inventories",
 
1933
                (path, ordering), body))
 
1934
        if response_tuple[0] != "ok":
2015
1935
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2016
1936
        deserializer = inventory_delta.InventoryDeltaDeserializer()
2017
1937
        byte_stream = response_handler.read_streamed_body()
2023
1943
        if src_format.network_name() != self._format.network_name():
2024
1944
            raise AssertionError(
2025
1945
                "Mismatched RemoteRepository and stream src %r, %r" % (
2026
 
                    src_format.network_name(), self._format.network_name()))
 
1946
                src_format.network_name(), self._format.network_name()))
2027
1947
        # ignore the src format, it's not really relevant
2028
1948
        prev_inv = Inventory(root_id=None,
2029
 
                             revision_id=_mod_revision.NULL_REVISION)
 
1949
            revision_id=_mod_revision.NULL_REVISION)
2030
1950
        # there should be just one substream, with inventory deltas
2031
 
        try:
2032
 
            substream_kind, substream = next(stream)
2033
 
        except StopIteration:
2034
 
            return
 
1951
        substream_kind, substream = stream.next()
2035
1952
        if substream_kind != "inventory-deltas":
2036
1953
            raise AssertionError(
2037
 
                "Unexpected stream %r received" % substream_kind)
 
1954
                 "Unexpected stream %r received" % substream_kind)
2038
1955
        for record in substream:
2039
1956
            (parent_id, new_id, versioned_root, tree_references, invdelta) = (
2040
 
                deserializer.parse_text_bytes(record.get_bytes_as("lines")))
 
1957
                deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
2041
1958
            if parent_id != prev_inv.revision_id:
2042
1959
                raise AssertionError("invalid base %r != %r" % (parent_id,
2043
 
                                                                prev_inv.revision_id))
 
1960
                    prev_inv.revision_id))
2044
1961
            inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2045
1962
            yield inv, inv.revision_id
2046
1963
            prev_inv = inv
2062
1979
            buffering if necessary).
2063
1980
        :return: An iterator of inventories.
2064
1981
        """
2065
 
        if ((None in revision_ids) or
2066
 
                (_mod_revision.NULL_REVISION in revision_ids)):
 
1982
        if ((None in revision_ids)
 
1983
            or (_mod_revision.NULL_REVISION in revision_ids)):
2067
1984
            raise ValueError('cannot get null revision inventory')
2068
1985
        for inv, revid in self._iter_inventories(revision_ids, ordering):
2069
1986
            if inv is None:
2125
2042
            while missing:
2126
2043
                yield None, missing.pop()
2127
2044
 
 
2045
    @needs_read_lock
2128
2046
    def get_revision(self, revision_id):
2129
 
        with self.lock_read():
2130
 
            return self.get_revisions([revision_id])[0]
 
2047
        return self.get_revisions([revision_id])[0]
2131
2048
 
2132
2049
    def get_transaction(self):
2133
2050
        self._ensure_real()
2134
2051
        return self._real_repository.get_transaction()
2135
2052
 
2136
 
    def clone(self, a_controldir, revision_id=None):
2137
 
        with self.lock_read():
2138
 
            dest_repo = self._create_sprouting_repo(
2139
 
                a_controldir, shared=self.is_shared())
2140
 
            self.copy_content_into(dest_repo, revision_id)
2141
 
            return dest_repo
 
2053
    @needs_read_lock
 
2054
    def clone(self, a_bzrdir, revision_id=None):
 
2055
        dest_repo = self._create_sprouting_repo(
 
2056
            a_bzrdir, shared=self.is_shared())
 
2057
        self.copy_content_into(dest_repo, revision_id)
 
2058
        return dest_repo
2142
2059
 
2143
2060
    def make_working_trees(self):
2144
2061
        """See Repository.make_working_trees"""
2145
 
        path = self.controldir._path_for_remote_call(self._client)
 
2062
        path = self.bzrdir._path_for_remote_call(self._client)
2146
2063
        try:
2147
 
            response = self._call(b'Repository.make_working_trees', path)
 
2064
            response = self._call('Repository.make_working_trees', path)
2148
2065
        except errors.UnknownSmartMethod:
2149
2066
            self._ensure_real()
2150
2067
            return self._real_repository.make_working_trees()
2151
 
        if response[0] not in (b'yes', b'no'):
2152
 
            raise SmartProtocolError(
2153
 
                'unexpected response code %s' % (response,))
2154
 
        return response[0] == b'yes'
 
2068
        if response[0] not in ('yes', 'no'):
 
2069
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
2070
        return response[0] == 'yes'
2155
2071
 
2156
2072
    def refresh_data(self):
2157
2073
        """Re-read any data needed to synchronise with disk.
2159
2075
        This method is intended to be called after another repository instance
2160
2076
        (such as one used by a smart server) has inserted data into the
2161
2077
        repository. On all repositories this will work outside of write groups.
2162
 
        Some repository formats (pack and newer for breezy native formats)
 
2078
        Some repository formats (pack and newer for brzlib native formats)
2163
2079
        support refresh_data inside write groups. If called inside a write
2164
2080
        group on a repository that does not support refreshing in a write group
2165
2081
        IsInWriteGroupError will be raised.
2173
2089
    def revision_ids_to_search_result(self, result_set):
2174
2090
        """Convert a set of revision ids to a graph SearchResult."""
2175
2091
        result_parents = set()
2176
 
        for parents in self.get_graph().get_parent_map(result_set).values():
 
2092
        for parents in self.get_graph().get_parent_map(
 
2093
            result_set).itervalues():
2177
2094
            result_parents.update(parents)
2178
2095
        included_keys = result_set.intersection(result_parents)
2179
2096
        start_keys = result_set.difference(included_keys)
2180
2097
        exclude_keys = result_parents.difference(result_set)
2181
2098
        result = vf_search.SearchResult(start_keys, exclude_keys,
2182
 
                                        len(result_set), result_set)
 
2099
            len(result_set), result_set)
2183
2100
        return result
2184
2101
 
 
2102
    @needs_read_lock
2185
2103
    def search_missing_revision_ids(self, other,
2186
 
                                    find_ghosts=True, revision_ids=None, if_present_ids=None,
2187
 
                                    limit=None):
 
2104
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
2105
            find_ghosts=True, revision_ids=None, if_present_ids=None,
 
2106
            limit=None):
2188
2107
        """Return the revision ids that other has that this does not.
2189
2108
 
2190
2109
        These are returned in topological order.
2191
2110
 
2192
2111
        revision_id: only return revision ids included by revision_id.
2193
2112
        """
2194
 
        with self.lock_read():
2195
 
            inter_repo = _mod_repository.InterRepository.get(other, self)
2196
 
            return inter_repo.search_missing_revision_ids(
2197
 
                find_ghosts=find_ghosts, revision_ids=revision_ids,
2198
 
                if_present_ids=if_present_ids, limit=limit)
 
2113
        if symbol_versioning.deprecated_passed(revision_id):
 
2114
            symbol_versioning.warn(
 
2115
                'search_missing_revision_ids(revision_id=...) was '
 
2116
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
2117
                DeprecationWarning, stacklevel=2)
 
2118
            if revision_ids is not None:
 
2119
                raise AssertionError(
 
2120
                    'revision_ids is mutually exclusive with revision_id')
 
2121
            if revision_id is not None:
 
2122
                revision_ids = [revision_id]
 
2123
        inter_repo = _mod_repository.InterRepository.get(other, self)
 
2124
        return inter_repo.search_missing_revision_ids(
 
2125
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
2126
            if_present_ids=if_present_ids, limit=limit)
2199
2127
 
2200
2128
    def fetch(self, source, revision_id=None, find_ghosts=False,
2201
 
              fetch_spec=None, lossy=False):
 
2129
            fetch_spec=None):
2202
2130
        # No base implementation to use as RemoteRepository is not a subclass
2203
2131
        # of Repository; so this is a copy of Repository.fetch().
2204
2132
        if fetch_spec is not None and revision_id is not None:
2208
2136
            raise errors.InternalBzrError(
2209
2137
                "May not fetch while in a write group.")
2210
2138
        # fast path same-url fetch operations
2211
 
        if (self.has_same_location(source) and
2212
 
            fetch_spec is None and
2213
 
                self._has_same_fallbacks(source)):
 
2139
        if (self.has_same_location(source)
 
2140
            and fetch_spec is None
 
2141
            and self._has_same_fallbacks(source)):
2214
2142
            # check that last_revision is in 'from' and then return a
2215
2143
            # no-operation.
2216
 
            if (revision_id is not None
2217
 
                    and not _mod_revision.is_null(revision_id)):
 
2144
            if (revision_id is not None and
 
2145
                not _mod_revision.is_null(revision_id)):
2218
2146
                self.get_revision(revision_id)
2219
 
            return _mod_repository.FetchResult(0)
 
2147
            return 0, []
2220
2148
        # if there is no specific appropriate InterRepository, this will get
2221
2149
        # the InterRepository base class, which raises an
2222
2150
        # IncompatibleRepositories when asked to fetch.
2223
2151
        inter = _mod_repository.InterRepository.get(source, self)
2224
 
        if (fetch_spec is not None
2225
 
                and not getattr(inter, "supports_fetch_spec", False)):
 
2152
        if (fetch_spec is not None and
 
2153
            not getattr(inter, "supports_fetch_spec", False)):
2226
2154
            raise errors.UnsupportedOperation(
2227
2155
                "fetch_spec not supported for %r" % inter)
2228
2156
        return inter.fetch(revision_id=revision_id,
2229
 
                           find_ghosts=find_ghosts, fetch_spec=fetch_spec,
2230
 
                           lossy=lossy)
 
2157
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
2231
2158
 
2232
2159
    def create_bundle(self, target, base, fileobj, format=None):
2233
2160
        self._ensure_real()
2243
2170
            revisions, revision_versions_cache)
2244
2171
 
2245
2172
    def _iter_files_bytes_rpc(self, desired_files, absent):
2246
 
        path = self.controldir._path_for_remote_call(self._client)
 
2173
        path = self.bzrdir._path_for_remote_call(self._client)
2247
2174
        lines = []
2248
2175
        identifiers = []
2249
2176
        for (file_id, revid, identifier) in desired_files:
2250
 
            lines.append(b''.join([
 
2177
            lines.append("%s\0%s" % (
2251
2178
                osutils.safe_file_id(file_id),
2252
 
                b'\0',
2253
 
                osutils.safe_revision_id(revid)]))
 
2179
                osutils.safe_revision_id(revid)))
2254
2180
            identifiers.append(identifier)
2255
2181
        (response_tuple, response_handler) = (
2256
2182
            self._call_with_body_bytes_expecting_body(
2257
 
                b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2258
 
        if response_tuple != (b'ok', ):
 
2183
            "Repository.iter_files_bytes", (path, ), "\n".join(lines)))
 
2184
        if response_tuple != ('ok', ):
2259
2185
            response_handler.cancel_read_body()
2260
2186
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2261
2187
        byte_stream = response_handler.read_streamed_body()
2262
 
 
2263
2188
        def decompress_stream(start, byte_stream, unused):
2264
2189
            decompressor = zlib.decompressobj()
2265
2190
            yield decompressor.decompress(start)
2266
 
            while decompressor.unused_data == b"":
 
2191
            while decompressor.unused_data == "":
2267
2192
                try:
2268
 
                    data = next(byte_stream)
 
2193
                    data = byte_stream.next()
2269
2194
                except StopIteration:
2270
2195
                    break
2271
2196
                yield decompressor.decompress(data)
2272
2197
            yield decompressor.flush()
2273
2198
            unused.append(decompressor.unused_data)
2274
 
        unused = b""
 
2199
        unused = ""
2275
2200
        while True:
2276
 
            while b"\n" not in unused:
2277
 
                try:
2278
 
                    unused += next(byte_stream)
2279
 
                except StopIteration:
2280
 
                    return
2281
 
            header, rest = unused.split(b"\n", 1)
2282
 
            args = header.split(b"\0")
2283
 
            if args[0] == b"absent":
 
2201
            while not "\n" in unused:
 
2202
                unused += byte_stream.next()
 
2203
            header, rest = unused.split("\n", 1)
 
2204
            args = header.split("\0")
 
2205
            if args[0] == "absent":
2284
2206
                absent[identifiers[int(args[3])]] = (args[1], args[2])
2285
2207
                unused = rest
2286
2208
                continue
2287
 
            elif args[0] == b"ok":
 
2209
            elif args[0] == "ok":
2288
2210
                idx = int(args[1])
2289
2211
            else:
2290
2212
                raise errors.UnexpectedSmartServerResponse(args)
2291
2213
            unused_chunks = []
2292
2214
            yield (identifiers[idx],
2293
 
                   decompress_stream(rest, byte_stream, unused_chunks))
2294
 
            unused = b"".join(unused_chunks)
 
2215
                decompress_stream(rest, byte_stream, unused_chunks))
 
2216
            unused = "".join(unused_chunks)
2295
2217
 
2296
2218
    def iter_files_bytes(self, desired_files):
2297
2219
        """See Repository.iter_file_bytes.
2304
2226
            for fallback in self._fallback_repositories:
2305
2227
                if not absent:
2306
2228
                    break
2307
 
                desired_files = [(key[0], key[1], identifier)
2308
 
                                 for identifier, key in absent.items()]
 
2229
                desired_files = [(key[0], key[1], identifier) for
 
2230
                    (identifier, key) in absent.iteritems()]
2309
2231
                for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2310
2232
                    del absent[identifier]
2311
2233
                    yield identifier, bytes_iterator
2312
2234
            if absent:
2313
2235
                # There may be more missing items, but raise an exception
2314
2236
                # for just one.
2315
 
                missing_identifier = next(iter(absent))
 
2237
                missing_identifier = absent.keys()[0]
2316
2238
                missing_key = absent[missing_identifier]
2317
2239
                raise errors.RevisionNotPresent(revision_id=missing_key[1],
2318
 
                                                file_id=missing_key[0])
 
2240
                    file_id=missing_key[0])
2319
2241
        except errors.UnknownSmartMethod:
2320
2242
            self._ensure_real()
2321
2243
            for (identifier, bytes_iterator) in (
2322
 
                    self._real_repository.iter_files_bytes(desired_files)):
 
2244
                self._real_repository.iter_files_bytes(desired_files)):
2323
2245
                yield identifier, bytes_iterator
2324
2246
 
2325
2247
    def get_cached_parent_map(self, revision_ids):
2326
 
        """See breezy.CachingParentsProvider.get_cached_parent_map"""
 
2248
        """See brzlib.CachingParentsProvider.get_cached_parent_map"""
2327
2249
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
2328
2250
 
2329
2251
    def get_parent_map(self, revision_ids):
2330
 
        """See breezy.Graph.get_parent_map()."""
 
2252
        """See brzlib.Graph.get_parent_map()."""
2331
2253
        return self._make_parents_provider().get_parent_map(revision_ids)
2332
2254
 
2333
2255
    def _get_parent_map_rpc(self, keys):
2352
2274
            # There is one other "bug" which is that ghosts in
2353
2275
            # get_revision_graph() are not returned at all. But we won't worry
2354
2276
            # about that for now.
2355
 
            for node_id, parent_ids in rg.items():
 
2277
            for node_id, parent_ids in rg.iteritems():
2356
2278
                if parent_ids == ():
2357
2279
                    rg[node_id] = (NULL_REVISION,)
2358
2280
            rg[NULL_REVISION] = ()
2363
2285
            raise ValueError('get_parent_map(None) is not valid')
2364
2286
        if NULL_REVISION in keys:
2365
2287
            keys.discard(NULL_REVISION)
2366
 
            found_parents = {NULL_REVISION: ()}
 
2288
            found_parents = {NULL_REVISION:()}
2367
2289
            if not keys:
2368
2290
                return found_parents
2369
2291
        else:
2400
2322
                keys, depth=_DEFAULT_SEARCH_DEPTH)
2401
2323
        recipe = ('manual', start_set, stop_keys, key_count)
2402
2324
        body = self._serialise_search_recipe(recipe)
2403
 
        path = self.controldir._path_for_remote_call(self._client)
 
2325
        path = self.bzrdir._path_for_remote_call(self._client)
2404
2326
        for key in keys:
2405
 
            if not isinstance(key, bytes):
 
2327
            if type(key) is not str:
2406
2328
                raise ValueError(
2407
 
                    "key %r not a bytes string" % (key,))
2408
 
        verb = b'Repository.get_parent_map'
2409
 
        args = (path, b'include-missing:') + tuple(keys)
 
2329
                    "key %r not a plain string" % (key,))
 
2330
        verb = 'Repository.get_parent_map'
 
2331
        args = (path, 'include-missing:') + tuple(keys)
2410
2332
        try:
2411
2333
            response = self._call_with_body_bytes_expecting_body(
2412
2334
                verb, args, body)
2425
2347
            # Recurse just once and we should use the fallback code.
2426
2348
            return self._get_parent_map_rpc(keys)
2427
2349
        response_tuple, response_handler = response
2428
 
        if response_tuple[0] not in [b'ok']:
 
2350
        if response_tuple[0] not in ['ok']:
2429
2351
            response_handler.cancel_read_body()
2430
2352
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2431
 
        if response_tuple[0] == b'ok':
 
2353
        if response_tuple[0] == 'ok':
2432
2354
            coded = bz2.decompress(response_handler.read_body_bytes())
2433
 
            if coded == b'':
 
2355
            if coded == '':
2434
2356
                # no revisions found
2435
2357
                return {}
2436
 
            lines = coded.split(b'\n')
 
2358
            lines = coded.split('\n')
2437
2359
            revision_graph = {}
2438
2360
            for line in lines:
2439
2361
                d = tuple(line.split())
2441
2363
                    revision_graph[d[0]] = d[1:]
2442
2364
                else:
2443
2365
                    # No parents:
2444
 
                    if d[0].startswith(b'missing:'):
 
2366
                    if d[0].startswith('missing:'):
2445
2367
                        revid = d[0][8:]
2446
2368
                        self._unstacked_provider.note_missing_key(revid)
2447
2369
                    else:
2450
2372
                        revision_graph[d[0]] = (NULL_REVISION,)
2451
2373
            return revision_graph
2452
2374
 
 
2375
    @needs_read_lock
2453
2376
    def get_signature_text(self, revision_id):
2454
 
        with self.lock_read():
2455
 
            path = self.controldir._path_for_remote_call(self._client)
2456
 
            try:
2457
 
                response_tuple, response_handler = self._call_expecting_body(
2458
 
                    b'Repository.get_revision_signature_text', path, revision_id)
2459
 
            except errors.UnknownSmartMethod:
2460
 
                self._ensure_real()
2461
 
                return self._real_repository.get_signature_text(revision_id)
2462
 
            except errors.NoSuchRevision as err:
2463
 
                for fallback in self._fallback_repositories:
2464
 
                    try:
2465
 
                        return fallback.get_signature_text(revision_id)
2466
 
                    except errors.NoSuchRevision:
2467
 
                        pass
2468
 
                raise err
2469
 
            else:
2470
 
                if response_tuple[0] != b'ok':
2471
 
                    raise errors.UnexpectedSmartServerResponse(response_tuple)
2472
 
                return response_handler.read_body_bytes()
 
2377
        path = self.bzrdir._path_for_remote_call(self._client)
 
2378
        try:
 
2379
            response_tuple, response_handler = self._call_expecting_body(
 
2380
                'Repository.get_revision_signature_text', path, revision_id)
 
2381
        except errors.UnknownSmartMethod:
 
2382
            self._ensure_real()
 
2383
            return self._real_repository.get_signature_text(revision_id)
 
2384
        except errors.NoSuchRevision, err:
 
2385
            for fallback in self._fallback_repositories:
 
2386
                try:
 
2387
                    return fallback.get_signature_text(revision_id)
 
2388
                except errors.NoSuchRevision:
 
2389
                    pass
 
2390
            raise err
 
2391
        else:
 
2392
            if response_tuple[0] != 'ok':
 
2393
                raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2394
            return response_handler.read_body_bytes()
2473
2395
 
 
2396
    @needs_read_lock
2474
2397
    def _get_inventory_xml(self, revision_id):
2475
 
        with self.lock_read():
2476
 
            # This call is used by older working tree formats,
2477
 
            # which stored a serialized basis inventory.
2478
 
            self._ensure_real()
2479
 
            return self._real_repository._get_inventory_xml(revision_id)
 
2398
        # This call is used by older working tree formats,
 
2399
        # which stored a serialized basis inventory.
 
2400
        self._ensure_real()
 
2401
        return self._real_repository._get_inventory_xml(revision_id)
2480
2402
 
 
2403
    @needs_write_lock
2481
2404
    def reconcile(self, other=None, thorough=False):
2482
 
        from ..reconcile import ReconcileResult
2483
 
        with self.lock_write():
2484
 
            path = self.controldir._path_for_remote_call(self._client)
2485
 
            try:
2486
 
                response, handler = self._call_expecting_body(
2487
 
                    b'Repository.reconcile', path, self._lock_token)
2488
 
            except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2489
 
                self._ensure_real()
2490
 
                return self._real_repository.reconcile(other=other, thorough=thorough)
2491
 
            if response != (b'ok', ):
2492
 
                raise errors.UnexpectedSmartServerResponse(response)
2493
 
            body = handler.read_body_bytes()
2494
 
            result = ReconcileResult()
2495
 
            result.garbage_inventories = None
2496
 
            result.inconsistent_parents = None
2497
 
            result.aborted = None
2498
 
            for line in body.split(b'\n'):
2499
 
                if not line:
2500
 
                    continue
2501
 
                key, val_text = line.split(b':')
2502
 
                if key == b"garbage_inventories":
2503
 
                    result.garbage_inventories = int(val_text)
2504
 
                elif key == b"inconsistent_parents":
2505
 
                    result.inconsistent_parents = int(val_text)
2506
 
                else:
2507
 
                    mutter("unknown reconcile key %r" % key)
2508
 
            return result
 
2405
        from brzlib.reconcile import RepoReconciler
 
2406
        path = self.bzrdir._path_for_remote_call(self._client)
 
2407
        try:
 
2408
            response, handler = self._call_expecting_body(
 
2409
                'Repository.reconcile', path, self._lock_token)
 
2410
        except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
 
2411
            self._ensure_real()
 
2412
            return self._real_repository.reconcile(other=other, thorough=thorough)
 
2413
        if response != ('ok', ):
 
2414
            raise errors.UnexpectedSmartServerResponse(response)
 
2415
        body = handler.read_body_bytes()
 
2416
        result = RepoReconciler(self)
 
2417
        for line in body.split('\n'):
 
2418
            if not line:
 
2419
                continue
 
2420
            key, val_text = line.split(':')
 
2421
            if key == "garbage_inventories":
 
2422
                result.garbage_inventories = int(val_text)
 
2423
            elif key == "inconsistent_parents":
 
2424
                result.inconsistent_parents = int(val_text)
 
2425
            else:
 
2426
                mutter("unknown reconcile key %r" % key)
 
2427
        return result
2509
2428
 
2510
2429
    def all_revision_ids(self):
2511
 
        path = self.controldir._path_for_remote_call(self._client)
 
2430
        path = self.bzrdir._path_for_remote_call(self._client)
2512
2431
        try:
2513
2432
            response_tuple, response_handler = self._call_expecting_body(
2514
 
                b"Repository.all_revision_ids", path)
 
2433
                "Repository.all_revision_ids", path)
2515
2434
        except errors.UnknownSmartMethod:
2516
2435
            self._ensure_real()
2517
2436
            return self._real_repository.all_revision_ids()
2518
 
        if response_tuple != (b"ok", ):
 
2437
        if response_tuple != ("ok", ):
2519
2438
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2520
2439
        revids = set(response_handler.read_body_bytes().splitlines())
2521
2440
        for fallback in self._fallback_repositories:
2526
2445
        """Return Tree for a revision on this branch with only some files.
2527
2446
 
2528
2447
        :param revision_ids: a sequence of revision-ids;
2529
 
          a revision-id may not be None or b'null:'
 
2448
          a revision-id may not be None or 'null:'
2530
2449
        :param file_ids: if not None, the result is filtered
2531
2450
          so that only those file-ids, their parents and their
2532
2451
          children are included.
2538
2457
            filtered_inv = inv.filter(file_ids)
2539
2458
            yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2540
2459
 
 
2460
    @needs_read_lock
2541
2461
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2542
 
        with self.lock_read():
2543
 
            medium = self._client._medium
2544
 
            if medium._is_remote_before((1, 2)):
2545
 
                self._ensure_real()
2546
 
                for delta in self._real_repository.get_deltas_for_revisions(
2547
 
                        revisions, specific_fileids):
2548
 
                    yield delta
2549
 
                return
2550
 
            # Get the revision-ids of interest
2551
 
            required_trees = set()
2552
 
            for revision in revisions:
2553
 
                required_trees.add(revision.revision_id)
2554
 
                required_trees.update(revision.parent_ids[:1])
2555
 
 
2556
 
            # Get the matching filtered trees. Note that it's more
2557
 
            # efficient to pass filtered trees to changes_from() rather
2558
 
            # than doing the filtering afterwards. changes_from() could
2559
 
            # arguably do the filtering itself but it's path-based, not
2560
 
            # file-id based, so filtering before or afterwards is
2561
 
            # currently easier.
2562
 
            if specific_fileids is None:
2563
 
                trees = dict((t.get_revision_id(), t) for
2564
 
                             t in self.revision_trees(required_trees))
 
2462
        medium = self._client._medium
 
2463
        if medium._is_remote_before((1, 2)):
 
2464
            self._ensure_real()
 
2465
            for delta in self._real_repository.get_deltas_for_revisions(
 
2466
                    revisions, specific_fileids):
 
2467
                yield delta
 
2468
            return
 
2469
        # Get the revision-ids of interest
 
2470
        required_trees = set()
 
2471
        for revision in revisions:
 
2472
            required_trees.add(revision.revision_id)
 
2473
            required_trees.update(revision.parent_ids[:1])
 
2474
 
 
2475
        # Get the matching filtered trees. Note that it's more
 
2476
        # efficient to pass filtered trees to changes_from() rather
 
2477
        # than doing the filtering afterwards. changes_from() could
 
2478
        # arguably do the filtering itself but it's path-based, not
 
2479
        # file-id based, so filtering before or afterwards is
 
2480
        # currently easier.
 
2481
        if specific_fileids is None:
 
2482
            trees = dict((t.get_revision_id(), t) for
 
2483
                t in self.revision_trees(required_trees))
 
2484
        else:
 
2485
            trees = dict((t.get_revision_id(), t) for
 
2486
                t in self._filtered_revision_trees(required_trees,
 
2487
                specific_fileids))
 
2488
 
 
2489
        # Calculate the deltas
 
2490
        for revision in revisions:
 
2491
            if not revision.parent_ids:
 
2492
                old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2565
2493
            else:
2566
 
                trees = dict((t.get_revision_id(), t) for
2567
 
                             t in self._filtered_revision_trees(required_trees,
2568
 
                                                                specific_fileids))
2569
 
 
2570
 
            # Calculate the deltas
2571
 
            for revision in revisions:
2572
 
                if not revision.parent_ids:
2573
 
                    old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2574
 
                else:
2575
 
                    old_tree = trees[revision.parent_ids[0]]
2576
 
                yield trees[revision.revision_id].changes_from(old_tree)
2577
 
 
2578
 
    def get_revision_delta(self, revision_id):
2579
 
        with self.lock_read():
2580
 
            r = self.get_revision(revision_id)
2581
 
            return list(self.get_deltas_for_revisions([r]))[0]
2582
 
 
 
2494
                old_tree = trees[revision.parent_ids[0]]
 
2495
            yield trees[revision.revision_id].changes_from(old_tree)
 
2496
 
 
2497
    @needs_read_lock
 
2498
    def get_revision_delta(self, revision_id, specific_fileids=None):
 
2499
        r = self.get_revision(revision_id)
 
2500
        return list(self.get_deltas_for_revisions([r],
 
2501
            specific_fileids=specific_fileids))[0]
 
2502
 
 
2503
    @needs_read_lock
2583
2504
    def revision_trees(self, revision_ids):
2584
 
        with self.lock_read():
2585
 
            inventories = self.iter_inventories(revision_ids)
2586
 
            for inv in inventories:
2587
 
                yield RemoteInventoryTree(self, inv, inv.revision_id)
 
2505
        inventories = self.iter_inventories(revision_ids)
 
2506
        for inv in inventories:
 
2507
            yield InventoryRevisionTree(self, inv, inv.revision_id)
2588
2508
 
 
2509
    @needs_read_lock
2589
2510
    def get_revision_reconcile(self, revision_id):
2590
 
        with self.lock_read():
2591
 
            self._ensure_real()
2592
 
            return self._real_repository.get_revision_reconcile(revision_id)
 
2511
        self._ensure_real()
 
2512
        return self._real_repository.get_revision_reconcile(revision_id)
2593
2513
 
 
2514
    @needs_read_lock
2594
2515
    def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2595
 
        with self.lock_read():
2596
 
            self._ensure_real()
2597
 
            return self._real_repository.check(revision_ids=revision_ids,
2598
 
                                               callback_refs=callback_refs, check_repo=check_repo)
 
2516
        self._ensure_real()
 
2517
        return self._real_repository.check(revision_ids=revision_ids,
 
2518
            callback_refs=callback_refs, check_repo=check_repo)
2599
2519
 
2600
2520
    def copy_content_into(self, destination, revision_id=None):
2601
2521
        """Make a complete copy of the content in self into destination.
2618
2538
        destination = to_bzrdir.create_repository()
2619
2539
        try:
2620
2540
            tar = tarfile.open('repository', fileobj=tar_file,
2621
 
                               mode='r|bz2')
 
2541
                mode='r|bz2')
2622
2542
            tmpdir = osutils.mkdtemp()
2623
2543
            try:
2624
 
                tar.extractall(tmpdir)
 
2544
                _extract_tar(tar, tmpdir)
2625
2545
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
2626
2546
                tmp_repo = tmp_bzrdir.open_repository()
2627
2547
                tmp_repo.copy_content_into(destination, revision_id)
2643
2563
        self._ensure_real()
2644
2564
        return self._real_repository.inventories
2645
2565
 
 
2566
    @needs_write_lock
2646
2567
    def pack(self, hint=None, clean_obsolete_packs=False):
2647
2568
        """Compress the data within the repository.
2648
2569
        """
2649
2570
        if hint is None:
2650
 
            body = b""
 
2571
            body = ""
2651
2572
        else:
2652
 
            body = b"".join([l.encode('ascii') + b"\n" for l in hint])
2653
 
        with self.lock_write():
2654
 
            path = self.controldir._path_for_remote_call(self._client)
2655
 
            try:
2656
 
                response, handler = self._call_with_body_bytes_expecting_body(
2657
 
                    b'Repository.pack', (path, self._lock_token,
2658
 
                                         str(clean_obsolete_packs).encode('ascii')), body)
2659
 
            except errors.UnknownSmartMethod:
2660
 
                self._ensure_real()
2661
 
                return self._real_repository.pack(hint=hint,
2662
 
                                                  clean_obsolete_packs=clean_obsolete_packs)
2663
 
            handler.cancel_read_body()
2664
 
            if response != (b'ok', ):
2665
 
                raise errors.UnexpectedSmartServerResponse(response)
 
2573
            body = "".join([l+"\n" for l in hint])
 
2574
        path = self.bzrdir._path_for_remote_call(self._client)
 
2575
        try:
 
2576
            response, handler = self._call_with_body_bytes_expecting_body(
 
2577
                'Repository.pack', (path, self._lock_token,
 
2578
                    str(clean_obsolete_packs)), body)
 
2579
        except errors.UnknownSmartMethod:
 
2580
            self._ensure_real()
 
2581
            return self._real_repository.pack(hint=hint,
 
2582
                clean_obsolete_packs=clean_obsolete_packs)
 
2583
        handler.cancel_read_body()
 
2584
        if response != ('ok', ):
 
2585
            raise errors.UnexpectedSmartServerResponse(response)
2666
2586
 
2667
2587
    @property
2668
2588
    def revisions(self):
2675
2595
 
2676
2596
    def set_make_working_trees(self, new_value):
2677
2597
        if new_value:
2678
 
            new_value_str = b"True"
 
2598
            new_value_str = "True"
2679
2599
        else:
2680
 
            new_value_str = b"False"
2681
 
        path = self.controldir._path_for_remote_call(self._client)
 
2600
            new_value_str = "False"
 
2601
        path = self.bzrdir._path_for_remote_call(self._client)
2682
2602
        try:
2683
2603
            response = self._call(
2684
 
                b'Repository.set_make_working_trees', path, new_value_str)
 
2604
                'Repository.set_make_working_trees', path, new_value_str)
2685
2605
        except errors.UnknownSmartMethod:
2686
2606
            self._ensure_real()
2687
2607
            self._real_repository.set_make_working_trees(new_value)
2688
2608
        else:
2689
 
            if response[0] != b'ok':
 
2609
            if response[0] != 'ok':
2690
2610
                raise errors.UnexpectedSmartServerResponse(response)
2691
2611
 
2692
2612
    @property
2699
2619
        self._ensure_real()
2700
2620
        return self._real_repository.signatures
2701
2621
 
 
2622
    @needs_write_lock
2702
2623
    def sign_revision(self, revision_id, gpg_strategy):
2703
 
        with self.lock_write():
2704
 
            testament = _mod_testament.Testament.from_revision(
2705
 
                self, revision_id)
2706
 
            plaintext = testament.as_short_text()
2707
 
            self.store_revision_signature(gpg_strategy, plaintext, revision_id)
 
2624
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2625
        plaintext = testament.as_short_text()
 
2626
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
2708
2627
 
2709
2628
    @property
2710
2629
    def texts(self):
2717
2636
        return self._real_repository.texts
2718
2637
 
2719
2638
    def _iter_revisions_rpc(self, revision_ids):
2720
 
        body = b"\n".join(revision_ids)
2721
 
        path = self.controldir._path_for_remote_call(self._client)
 
2639
        body = "\n".join(revision_ids)
 
2640
        path = self.bzrdir._path_for_remote_call(self._client)
2722
2641
        response_tuple, response_handler = (
2723
2642
            self._call_with_body_bytes_expecting_body(
2724
 
                b"Repository.iter_revisions", (path, ), body))
2725
 
        if response_tuple[0] != b"ok":
 
2643
            "Repository.iter_revisions", (path, ), body))
 
2644
        if response_tuple[0] != "ok":
2726
2645
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2727
 
        serializer_format = response_tuple[1].decode('ascii')
 
2646
        serializer_format = response_tuple[1]
2728
2647
        serializer = serializer_format_registry.get(serializer_format)
2729
2648
        byte_stream = response_handler.read_streamed_body()
2730
2649
        decompressor = zlib.decompressobj()
2731
2650
        chunks = []
2732
2651
        for bytes in byte_stream:
2733
2652
            chunks.append(decompressor.decompress(bytes))
2734
 
            if decompressor.unused_data != b"":
 
2653
            if decompressor.unused_data != "":
2735
2654
                chunks.append(decompressor.flush())
2736
 
                yield serializer.read_revision_from_string(b"".join(chunks))
 
2655
                yield serializer.read_revision_from_string("".join(chunks))
2737
2656
                unused = decompressor.unused_data
2738
2657
                decompressor = zlib.decompressobj()
2739
2658
                chunks = [decompressor.decompress(unused)]
2740
2659
        chunks.append(decompressor.flush())
2741
 
        text = b"".join(chunks)
2742
 
        if text != b"":
2743
 
            yield serializer.read_revision_from_string(b"".join(chunks))
 
2660
        text = "".join(chunks)
 
2661
        if text != "":
 
2662
            yield serializer.read_revision_from_string("".join(chunks))
2744
2663
 
2745
 
    def iter_revisions(self, revision_ids):
2746
 
        for rev_id in revision_ids:
2747
 
            if not rev_id or not isinstance(rev_id, bytes):
2748
 
                raise errors.InvalidRevisionId(
2749
 
                    revision_id=rev_id, branch=self)
2750
 
        with self.lock_read():
2751
 
            try:
2752
 
                missing = set(revision_ids)
2753
 
                for rev in self._iter_revisions_rpc(revision_ids):
2754
 
                    missing.remove(rev.revision_id)
2755
 
                    yield (rev.revision_id, rev)
2756
 
                for fallback in self._fallback_repositories:
2757
 
                    if not missing:
2758
 
                        break
2759
 
                    for (revid, rev) in fallback.iter_revisions(missing):
2760
 
                        if rev is not None:
2761
 
                            yield (revid, rev)
2762
 
                            missing.remove(revid)
2763
 
                for revid in missing:
2764
 
                    yield (revid, None)
2765
 
            except errors.UnknownSmartMethod:
2766
 
                self._ensure_real()
2767
 
                for entry in self._real_repository.iter_revisions(revision_ids):
2768
 
                    yield entry
 
2664
    @needs_read_lock
 
2665
    def get_revisions(self, revision_ids):
 
2666
        if revision_ids is None:
 
2667
            revision_ids = self.all_revision_ids()
 
2668
        else:
 
2669
            for rev_id in revision_ids:
 
2670
                if not rev_id or not isinstance(rev_id, basestring):
 
2671
                    raise errors.InvalidRevisionId(
 
2672
                        revision_id=rev_id, branch=self)
 
2673
        try:
 
2674
            missing = set(revision_ids)
 
2675
            revs = {}
 
2676
            for rev in self._iter_revisions_rpc(revision_ids):
 
2677
                missing.remove(rev.revision_id)
 
2678
                revs[rev.revision_id] = rev
 
2679
        except errors.UnknownSmartMethod:
 
2680
            self._ensure_real()
 
2681
            return self._real_repository.get_revisions(revision_ids)
 
2682
        for fallback in self._fallback_repositories:
 
2683
            if not missing:
 
2684
                break
 
2685
            for revid in list(missing):
 
2686
                # XXX JRV 2011-11-20: It would be nice if there was a
 
2687
                # public method on Repository that could be used to query
 
2688
                # for revision objects *without* failing completely if one
 
2689
                # was missing. There is VersionedFileRepository._iter_revisions,
 
2690
                # but unfortunately that's private and not provided by
 
2691
                # all repository implementations.
 
2692
                try:
 
2693
                    revs[revid] = fallback.get_revision(revid)
 
2694
                except errors.NoSuchRevision:
 
2695
                    pass
 
2696
                else:
 
2697
                    missing.remove(revid)
 
2698
        if missing:
 
2699
            raise errors.NoSuchRevision(self, list(missing)[0])
 
2700
        return [revs[revid] for revid in revision_ids]
2769
2701
 
2770
2702
    def supports_rich_root(self):
2771
2703
        return self._format.rich_root_data
2774
2706
    def _serializer(self):
2775
2707
        return self._format._serializer
2776
2708
 
 
2709
    @needs_write_lock
2777
2710
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2778
 
        with self.lock_write():
2779
 
            signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2780
 
            self.add_signature_text(revision_id, signature)
 
2711
        signature = gpg_strategy.sign(plaintext)
 
2712
        self.add_signature_text(revision_id, signature)
2781
2713
 
2782
2714
    def add_signature_text(self, revision_id, signature):
2783
2715
        if self._real_repository:
2786
2718
            self._ensure_real()
2787
2719
            return self._real_repository.add_signature_text(
2788
2720
                revision_id, signature)
2789
 
        path = self.controldir._path_for_remote_call(self._client)
 
2721
        path = self.bzrdir._path_for_remote_call(self._client)
2790
2722
        response, handler = self._call_with_body_bytes_expecting_body(
2791
 
            b'Repository.add_signature_text', (path, self._lock_token,
2792
 
                                               revision_id) +
2793
 
            tuple([token.encode('utf-8')
2794
 
                   for token in self._write_group_tokens]),
2795
 
            signature)
 
2723
            'Repository.add_signature_text', (path, self._lock_token,
 
2724
                revision_id) + tuple(self._write_group_tokens), signature)
2796
2725
        handler.cancel_read_body()
2797
2726
        self.refresh_data()
2798
 
        if response[0] != b'ok':
 
2727
        if response[0] != 'ok':
2799
2728
            raise errors.UnexpectedSmartServerResponse(response)
2800
 
        self._write_group_tokens = [token.decode(
2801
 
            'utf-8') for token in response[1:]]
 
2729
        self._write_group_tokens = response[1:]
2802
2730
 
2803
2731
    def has_signature_for_revision_id(self, revision_id):
2804
 
        path = self.controldir._path_for_remote_call(self._client)
 
2732
        path = self.bzrdir._path_for_remote_call(self._client)
2805
2733
        try:
2806
 
            response = self._call(b'Repository.has_signature_for_revision_id',
2807
 
                                  path, revision_id)
 
2734
            response = self._call('Repository.has_signature_for_revision_id',
 
2735
                path, revision_id)
2808
2736
        except errors.UnknownSmartMethod:
2809
2737
            self._ensure_real()
2810
2738
            return self._real_repository.has_signature_for_revision_id(
2811
2739
                revision_id)
2812
 
        if response[0] not in (b'yes', b'no'):
2813
 
            raise SmartProtocolError(
2814
 
                'unexpected response code %s' % (response,))
2815
 
        if response[0] == b'yes':
 
2740
        if response[0] not in ('yes', 'no'):
 
2741
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
2742
        if response[0] == 'yes':
2816
2743
            return True
2817
2744
        for fallback in self._fallback_repositories:
2818
2745
            if fallback.has_signature_for_revision_id(revision_id):
2819
2746
                return True
2820
2747
        return False
2821
2748
 
 
2749
    @needs_read_lock
2822
2750
    def verify_revision_signature(self, revision_id, gpg_strategy):
2823
 
        with self.lock_read():
2824
 
            if not self.has_signature_for_revision_id(revision_id):
2825
 
                return gpg.SIGNATURE_NOT_SIGNED, None
2826
 
            signature = self.get_signature_text(revision_id)
2827
 
 
2828
 
            testament = _mod_testament.Testament.from_revision(
2829
 
                self, revision_id)
2830
 
 
2831
 
            (status, key, signed_plaintext) = gpg_strategy.verify(signature)
2832
 
            if testament.as_short_text() != signed_plaintext:
2833
 
                return gpg.SIGNATURE_NOT_VALID, None
2834
 
            return (status, key)
 
2751
        if not self.has_signature_for_revision_id(revision_id):
 
2752
            return gpg.SIGNATURE_NOT_SIGNED, None
 
2753
        signature = self.get_signature_text(revision_id)
 
2754
 
 
2755
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2756
        plaintext = testament.as_short_text()
 
2757
 
 
2758
        return gpg_strategy.verify(signature, plaintext)
2835
2759
 
2836
2760
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2837
2761
        self._ensure_real()
2838
2762
        return self._real_repository.item_keys_introduced_by(revision_ids,
2839
 
                                                             _files_pb=_files_pb)
 
2763
            _files_pb=_files_pb)
2840
2764
 
2841
2765
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2842
2766
        self._ensure_real()
2860
2784
        :param recipe: A search recipe (start, stop, count).
2861
2785
        :return: Serialised bytes.
2862
2786
        """
2863
 
        start_keys = b' '.join(recipe[1])
2864
 
        stop_keys = b' '.join(recipe[2])
2865
 
        count = str(recipe[3]).encode('ascii')
2866
 
        return b'\n'.join((start_keys, stop_keys, count))
 
2787
        start_keys = ' '.join(recipe[1])
 
2788
        stop_keys = ' '.join(recipe[2])
 
2789
        count = str(recipe[3])
 
2790
        return '\n'.join((start_keys, stop_keys, count))
2867
2791
 
2868
2792
    def _serialise_search_result(self, search_result):
2869
2793
        parts = search_result.get_network_struct()
2870
 
        return b'\n'.join(parts)
 
2794
        return '\n'.join(parts)
2871
2795
 
2872
2796
    def autopack(self):
2873
 
        path = self.controldir._path_for_remote_call(self._client)
 
2797
        path = self.bzrdir._path_for_remote_call(self._client)
2874
2798
        try:
2875
 
            response = self._call(b'PackRepository.autopack', path)
 
2799
            response = self._call('PackRepository.autopack', path)
2876
2800
        except errors.UnknownSmartMethod:
2877
2801
            self._ensure_real()
2878
2802
            self._real_repository._pack_collection.autopack()
2879
2803
            return
2880
2804
        self.refresh_data()
2881
 
        if response[0] != b'ok':
2882
 
            raise errors.UnexpectedSmartServerResponse(response)
2883
 
 
2884
 
    def _revision_archive(self, revision_id, format, name, root, subdir,
2885
 
                          force_mtime=None):
2886
 
        path = self.controldir._path_for_remote_call(self._client)
2887
 
        format = format or ''
2888
 
        root = root or ''
2889
 
        subdir = subdir or ''
2890
 
        force_mtime = int(force_mtime) if force_mtime is not None else None
2891
 
        try:
2892
 
            response, protocol = self._call_expecting_body(
2893
 
                b'Repository.revision_archive', path,
2894
 
                revision_id,
2895
 
                format.encode('ascii'),
2896
 
                os.path.basename(name).encode('utf-8'),
2897
 
                root.encode('utf-8'),
2898
 
                subdir.encode('utf-8'),
2899
 
                force_mtime)
2900
 
        except errors.UnknownSmartMethod:
2901
 
            return None
2902
 
        if response[0] == b'ok':
2903
 
            return iter([protocol.read_body_bytes()])
2904
 
        raise errors.UnexpectedSmartServerResponse(response)
2905
 
 
2906
 
    def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2907
 
        path = self.controldir._path_for_remote_call(self._client)
2908
 
        tree_path = tree_path.encode('utf-8')
2909
 
        file_id = file_id or b''
2910
 
        default_revision = default_revision or b''
2911
 
        try:
2912
 
            response, handler = self._call_expecting_body(
2913
 
                b'Repository.annotate_file_revision', path,
2914
 
                revid, tree_path, file_id, default_revision)
2915
 
        except errors.UnknownSmartMethod:
2916
 
            return None
2917
 
        if response[0] != b'ok':
2918
 
            raise errors.UnexpectedSmartServerResponse(response)
2919
 
        return map(tuple, bencode.bdecode(handler.read_body_bytes()))
 
2805
        if response[0] != 'ok':
 
2806
            raise errors.UnexpectedSmartServerResponse(response)
2920
2807
 
2921
2808
 
2922
2809
class RemoteStreamSink(vf_repository.StreamSink):
2929
2816
            self.target_repo.autopack()
2930
2817
        return result
2931
2818
 
2932
 
    def insert_missing_keys(self, source, missing_keys):
2933
 
        if (isinstance(source, RemoteStreamSource)
2934
 
                and source.from_repository._client._medium == self.target_repo._client._medium):
2935
 
            # Streaming from and to the same medium is tricky, since we don't support
2936
 
            # more than one concurrent request. For now, just force VFS.
2937
 
            stream = source._get_real_stream_for_missing_keys(missing_keys)
2938
 
        else:
2939
 
            stream = source.get_stream_for_missing_keys(missing_keys)
2940
 
        return self.insert_stream_without_locking(stream,
2941
 
                                                  self.target_repo._format)
2942
 
 
2943
2819
    def insert_stream(self, stream, src_format, resume_tokens):
2944
2820
        target = self.target_repo
2945
2821
        target._unstacked_provider.missing_keys.clear()
2946
 
        candidate_calls = [(b'Repository.insert_stream_1.19', (1, 19))]
 
2822
        candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
2947
2823
        if target._lock_token:
2948
 
            candidate_calls.append(
2949
 
                (b'Repository.insert_stream_locked', (1, 14)))
2950
 
            lock_args = (target._lock_token or b'',)
 
2824
            candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
 
2825
            lock_args = (target._lock_token or '',)
2951
2826
        else:
2952
 
            candidate_calls.append((b'Repository.insert_stream', (1, 13)))
 
2827
            candidate_calls.append(('Repository.insert_stream', (1, 13)))
2953
2828
            lock_args = ()
2954
2829
        client = target._client
2955
2830
        medium = client._medium
2956
 
        path = target.controldir._path_for_remote_call(client)
 
2831
        path = target.bzrdir._path_for_remote_call(client)
2957
2832
        # Probe for the verb to use with an empty stream before sending the
2958
2833
        # real stream to it.  We do this both to avoid the risk of sending a
2959
2834
        # large request that is then rejected, and because we don't want to
2970
2845
            byte_stream = smart_repo._stream_to_byte_stream([], src_format)
2971
2846
            try:
2972
2847
                response = client.call_with_body_stream(
2973
 
                    (verb, path, b'') + lock_args, byte_stream)
 
2848
                    (verb, path, '') + lock_args, byte_stream)
2974
2849
            except errors.UnknownSmartMethod:
2975
2850
                medium._remember_remote_is_before(required_version)
2976
2851
            else:
2989
2864
            stream = self._stop_stream_if_inventory_delta(stream)
2990
2865
        byte_stream = smart_repo._stream_to_byte_stream(
2991
2866
            stream, src_format)
2992
 
        resume_tokens = b' '.join([token.encode('utf-8')
2993
 
                                   for token in resume_tokens])
 
2867
        resume_tokens = ' '.join(resume_tokens)
2994
2868
        response = client.call_with_body_stream(
2995
2869
            (verb, path, resume_tokens) + lock_args, byte_stream)
2996
 
        if response[0][0] not in (b'ok', b'missing-basis'):
 
2870
        if response[0][0] not in ('ok', 'missing-basis'):
2997
2871
            raise errors.UnexpectedSmartServerResponse(response)
2998
2872
        if self._last_substream is not None:
2999
2873
            # The stream included an inventory-delta record, but the remote
3001
2875
            # rest of the stream via VFS.
3002
2876
            self.target_repo.refresh_data()
3003
2877
            return self._resume_stream_with_vfs(response, src_format)
3004
 
        if response[0][0] == b'missing-basis':
 
2878
        if response[0][0] == 'missing-basis':
3005
2879
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
3006
 
            resume_tokens = [token.decode('utf-8') for token in tokens]
3007
 
            return resume_tokens, set((entry[0].decode('utf-8'), ) + entry[1:] for entry in missing_keys)
 
2880
            resume_tokens = tokens
 
2881
            return resume_tokens, set(missing_keys)
3008
2882
        else:
3009
2883
            self.target_repo.refresh_data()
3010
2884
            return [], set()
3013
2887
        """Resume sending a stream via VFS, first resending the record and
3014
2888
        substream that couldn't be sent via an insert_stream verb.
3015
2889
        """
3016
 
        if response[0][0] == b'missing-basis':
 
2890
        if response[0][0] == 'missing-basis':
3017
2891
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
3018
 
            tokens = [token.decode('utf-8') for token in tokens]
3019
2892
            # Ignore missing_keys, we haven't finished inserting yet
3020
2893
        else:
3021
2894
            tokens = []
3022
 
 
3023
2895
        def resume_substream():
3024
2896
            # Yield the substream that was interrupted.
3025
2897
            for record in self._last_substream:
3026
2898
                yield record
3027
2899
            self._last_substream = None
3028
 
 
3029
2900
        def resume_stream():
3030
2901
            # Finish sending the interrupted substream
3031
2902
            yield ('inventory-deltas', resume_substream())
3057
2928
    """Stream data from a remote server."""
3058
2929
 
3059
2930
    def get_stream(self, search):
3060
 
        if (self.from_repository._fallback_repositories
3061
 
                and self.to_format._fetch_order == 'topological'):
 
2931
        if (self.from_repository._fallback_repositories and
 
2932
            self.to_format._fetch_order == 'topological'):
3062
2933
            return self._real_stream(self.from_repository, search)
3063
2934
        sources = []
3064
2935
        seen = set()
3072
2943
            sources.append(repo)
3073
2944
        return self.missing_parents_chain(search, sources)
3074
2945
 
3075
 
    def _get_real_stream_for_missing_keys(self, missing_keys):
 
2946
    def get_stream_for_missing_keys(self, missing_keys):
3076
2947
        self.from_repository._ensure_real()
3077
2948
        real_repo = self.from_repository._real_repository
3078
2949
        real_source = real_repo._get_source(self.to_format)
3079
2950
        return real_source.get_stream_for_missing_keys(missing_keys)
3080
2951
 
3081
 
    def get_stream_for_missing_keys(self, missing_keys):
3082
 
        if not isinstance(self.from_repository, RemoteRepository):
3083
 
            return self._get_real_stream_for_missing_keys(missing_keys)
3084
 
        client = self.from_repository._client
3085
 
        medium = client._medium
3086
 
        if medium._is_remote_before((3, 0)):
3087
 
            return self._get_real_stream_for_missing_keys(missing_keys)
3088
 
        path = self.from_repository.controldir._path_for_remote_call(client)
3089
 
        args = (path, self.to_format.network_name())
3090
 
        search_bytes = b'\n'.join(
3091
 
            [b'%s\t%s' % (key[0].encode('utf-8'), key[1]) for key in missing_keys])
3092
 
        try:
3093
 
            response, handler = self.from_repository._call_with_body_bytes_expecting_body(
3094
 
                b'Repository.get_stream_for_missing_keys', args, search_bytes)
3095
 
        except (errors.UnknownSmartMethod, errors.UnknownFormatError):
3096
 
            return self._get_real_stream_for_missing_keys(missing_keys)
3097
 
        if response[0] != b'ok':
3098
 
            raise errors.UnexpectedSmartServerResponse(response)
3099
 
        byte_stream = handler.read_streamed_body()
3100
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
3101
 
                                                               self._record_counter)
3102
 
        if src_format.network_name() != self.from_repository._format.network_name():
3103
 
            raise AssertionError(
3104
 
                "Mismatched RemoteRepository and stream src %r, %r" % (
3105
 
                    src_format.network_name(), repo._format.network_name()))
3106
 
        return stream
3107
 
 
3108
2952
    def _real_stream(self, repo, search):
3109
2953
        """Get a stream for search from repo.
3110
2954
 
3140
2984
            return self._real_stream(repo, search)
3141
2985
        client = repo._client
3142
2986
        medium = client._medium
3143
 
        path = repo.controldir._path_for_remote_call(client)
 
2987
        path = repo.bzrdir._path_for_remote_call(client)
3144
2988
        search_bytes = repo._serialise_search_result(search)
3145
2989
        args = (path, self.to_format.network_name())
3146
2990
        candidate_verbs = [
3147
 
            (b'Repository.get_stream_1.19', (1, 19)),
3148
 
            (b'Repository.get_stream', (1, 13))]
 
2991
            ('Repository.get_stream_1.19', (1, 19)),
 
2992
            ('Repository.get_stream', (1, 13))]
3149
2993
 
3150
2994
        found_verb = False
3151
2995
        for verb, version in candidate_verbs:
3156
3000
                    verb, args, search_bytes)
3157
3001
            except errors.UnknownSmartMethod:
3158
3002
                medium._remember_remote_is_before(version)
3159
 
            except errors.UnknownErrorFromSmartServer as e:
 
3003
            except errors.UnknownErrorFromSmartServer, e:
3160
3004
                if isinstance(search, vf_search.EverythingResult):
3161
3005
                    error_verb = e.error_from_smart_server.error_verb
3162
 
                    if error_verb == b'BadSearch':
 
3006
                    if error_verb == 'BadSearch':
3163
3007
                        # Pre-2.4 servers don't support this sort of search.
3164
3008
                        # XXX: perhaps falling back to VFS on BadSearch is a
3165
3009
                        # good idea in general?  It might provide a little bit
3173
3017
                break
3174
3018
        if not found_verb:
3175
3019
            return self._real_stream(repo, search)
3176
 
        if response_tuple[0] != b'ok':
 
3020
        if response_tuple[0] != 'ok':
3177
3021
            raise errors.UnexpectedSmartServerResponse(response_tuple)
3178
3022
        byte_stream = response_handler.read_streamed_body()
3179
3023
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
3180
 
                                                               self._record_counter)
 
3024
            self._record_counter)
3181
3025
        if src_format.network_name() != repo._format.network_name():
3182
3026
            raise AssertionError(
3183
3027
                "Mismatched RemoteRepository and stream src %r, %r" % (
3184
 
                    src_format.network_name(), repo._format.network_name()))
 
3028
                src_format.network_name(), repo._format.network_name()))
3185
3029
        return stream
3186
3030
 
3187
3031
    def missing_parents_chain(self, search, sources):
3227
3071
    """
3228
3072
 
3229
3073
    def __init__(self, bzrdir, _client):
3230
 
        self.controldir = bzrdir
 
3074
        self.bzrdir = bzrdir
3231
3075
        self._client = _client
3232
3076
        self._need_find_modes = True
3233
3077
        LockableFiles.__init__(
3244
3088
 
3245
3089
    def __init__(self, network_name=None):
3246
3090
        super(RemoteBranchFormat, self).__init__()
3247
 
        self._matchingcontroldir = RemoteBzrDirFormat()
3248
 
        self._matchingcontroldir.set_branch_format(self)
 
3091
        self._matchingbzrdir = RemoteBzrDirFormat()
 
3092
        self._matchingbzrdir.set_branch_format(self)
3249
3093
        self._custom_format = None
3250
3094
        self._network_name = network_name
3251
3095
 
3252
3096
    def __eq__(self, other):
3253
 
        return (isinstance(other, RemoteBranchFormat)
3254
 
                and self.__dict__ == other.__dict__)
 
3097
        return (isinstance(other, RemoteBranchFormat) and
 
3098
            self.__dict__ == other.__dict__)
3255
3099
 
3256
3100
    def _ensure_real(self):
3257
3101
        if self._custom_format is None:
3260
3104
                    self._network_name)
3261
3105
            except KeyError:
3262
3106
                raise errors.UnknownFormatError(kind='branch',
3263
 
                                                format=self._network_name)
 
3107
                    format=self._network_name)
3264
3108
 
3265
3109
    def get_format_description(self):
3266
3110
        self._ensure_real()
3269
3113
    def network_name(self):
3270
3114
        return self._network_name
3271
3115
 
3272
 
    def open(self, a_controldir, name=None, ignore_fallbacks=False):
3273
 
        return a_controldir.open_branch(name=name,
3274
 
                                        ignore_fallbacks=ignore_fallbacks)
 
3116
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
 
3117
        return a_bzrdir.open_branch(name=name, 
 
3118
            ignore_fallbacks=ignore_fallbacks)
3275
3119
 
3276
 
    def _vfs_initialize(self, a_controldir, name, append_revisions_only,
 
3120
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only,
3277
3121
                        repository=None):
3278
3122
        # Initialisation when using a local bzrdir object, or a non-vfs init
3279
3123
        # method is not available on the server.
3280
3124
        # self._custom_format is always set - the start of initialize ensures
3281
3125
        # that.
3282
 
        if isinstance(a_controldir, RemoteBzrDir):
3283
 
            a_controldir._ensure_real()
3284
 
            result = self._custom_format.initialize(a_controldir._real_bzrdir,
3285
 
                                                    name=name, append_revisions_only=append_revisions_only,
3286
 
                                                    repository=repository)
 
3126
        if isinstance(a_bzrdir, RemoteBzrDir):
 
3127
            a_bzrdir._ensure_real()
 
3128
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
 
3129
                name=name, append_revisions_only=append_revisions_only,
 
3130
                repository=repository)
3287
3131
        else:
3288
3132
            # We assume the bzrdir is parameterised; it may not be.
3289
 
            result = self._custom_format.initialize(a_controldir, name=name,
3290
 
                                                    append_revisions_only=append_revisions_only,
3291
 
                                                    repository=repository)
3292
 
        if (isinstance(a_controldir, RemoteBzrDir)
3293
 
                and not isinstance(result, RemoteBranch)):
3294
 
            result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
 
3133
            result = self._custom_format.initialize(a_bzrdir, name=name,
 
3134
                append_revisions_only=append_revisions_only,
 
3135
                repository=repository)
 
3136
        if (isinstance(a_bzrdir, RemoteBzrDir) and
 
3137
            not isinstance(result, RemoteBranch)):
 
3138
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3295
3139
                                  name=name)
3296
3140
        return result
3297
3141
 
3298
 
    def initialize(self, a_controldir, name=None, repository=None,
 
3142
    def initialize(self, a_bzrdir, name=None, repository=None,
3299
3143
                   append_revisions_only=None):
3300
3144
        if name is None:
3301
 
            name = a_controldir._get_selected_branch()
 
3145
            name = a_bzrdir._get_selected_branch()
3302
3146
        # 1) get the network name to use.
3303
3147
        if self._custom_format:
3304
3148
            network_name = self._custom_format.network_name()
3305
3149
        else:
3306
 
            # Select the current breezy default and ask for that.
3307
 
            reference_bzrdir_format = controldir.format_registry.get(
3308
 
                'default')()
 
3150
            # Select the current brzlib default and ask for that.
 
3151
            reference_bzrdir_format = controldir.format_registry.get('default')()
3309
3152
            reference_format = reference_bzrdir_format.get_branch_format()
3310
3153
            self._custom_format = reference_format
3311
3154
            network_name = reference_format.network_name()
3312
3155
        # Being asked to create on a non RemoteBzrDir:
3313
 
        if not isinstance(a_controldir, RemoteBzrDir):
3314
 
            return self._vfs_initialize(a_controldir, name=name,
3315
 
                                        append_revisions_only=append_revisions_only,
3316
 
                                        repository=repository)
3317
 
        medium = a_controldir._client._medium
 
3156
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
3157
            return self._vfs_initialize(a_bzrdir, name=name,
 
3158
                append_revisions_only=append_revisions_only,
 
3159
                repository=repository)
 
3160
        medium = a_bzrdir._client._medium
3318
3161
        if medium._is_remote_before((1, 13)):
3319
 
            return self._vfs_initialize(a_controldir, name=name,
3320
 
                                        append_revisions_only=append_revisions_only,
3321
 
                                        repository=repository)
 
3162
            return self._vfs_initialize(a_bzrdir, name=name,
 
3163
                append_revisions_only=append_revisions_only,
 
3164
                repository=repository)
3322
3165
        # Creating on a remote bzr dir.
3323
3166
        # 2) try direct creation via RPC
3324
 
        path = a_controldir._path_for_remote_call(a_controldir._client)
 
3167
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
3325
3168
        if name != "":
3326
3169
            # XXX JRV20100304: Support creating colocated branches
3327
3170
            raise errors.NoColocatedBranchSupport(self)
3328
 
        verb = b'BzrDir.create_branch'
 
3171
        verb = 'BzrDir.create_branch'
3329
3172
        try:
3330
 
            response = a_controldir._call(verb, path, network_name)
 
3173
            response = a_bzrdir._call(verb, path, network_name)
3331
3174
        except errors.UnknownSmartMethod:
3332
3175
            # Fallback - use vfs methods
3333
3176
            medium._remember_remote_is_before((1, 13))
3334
 
            return self._vfs_initialize(a_controldir, name=name,
3335
 
                                        append_revisions_only=append_revisions_only,
3336
 
                                        repository=repository)
3337
 
        if response[0] != b'ok':
 
3177
            return self._vfs_initialize(a_bzrdir, name=name,
 
3178
                    append_revisions_only=append_revisions_only,
 
3179
                    repository=repository)
 
3180
        if response[0] != 'ok':
3338
3181
            raise errors.UnexpectedSmartServerResponse(response)
3339
3182
        # Turn the response into a RemoteRepository object.
3340
3183
        format = RemoteBranchFormat(network_name=response[1])
3341
3184
        repo_format = response_tuple_to_repo_format(response[3:])
3342
 
        repo_path = response[2].decode('utf-8')
 
3185
        repo_path = response[2]
3343
3186
        if repository is not None:
3344
 
            remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
 
3187
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
3345
3188
            url_diff = urlutils.relative_url(repository.user_url,
3346
 
                                             remote_repo_url)
 
3189
                    remote_repo_url)
3347
3190
            if url_diff != '.':
3348
3191
                raise AssertionError(
3349
3192
                    'repository.user_url %r does not match URL from server '
3350
3193
                    'response (%r + %r)'
3351
 
                    % (repository.user_url, a_controldir.user_url, repo_path))
 
3194
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
3352
3195
            remote_repo = repository
3353
3196
        else:
3354
3197
            if repo_path == '':
3355
 
                repo_bzrdir = a_controldir
 
3198
                repo_bzrdir = a_bzrdir
3356
3199
            else:
3357
3200
                repo_bzrdir = RemoteBzrDir(
3358
 
                    a_controldir.root_transport.clone(
3359
 
                        repo_path), a_controldir._format,
3360
 
                    a_controldir._client)
 
3201
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
 
3202
                    a_bzrdir._client)
3361
3203
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3362
 
        remote_branch = RemoteBranch(a_controldir, remote_repo,
3363
 
                                     format=format, setup_stacking=False, name=name)
 
3204
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
 
3205
            format=format, setup_stacking=False, name=name)
3364
3206
        if append_revisions_only:
3365
3207
            remote_branch.set_append_revisions_only(append_revisions_only)
3366
3208
        # XXX: We know this is a new branch, so it must have revno 0, revid
3387
3229
        self._ensure_real()
3388
3230
        return self._custom_format.supports_set_append_revisions_only()
3389
3231
 
3390
 
    @property
3391
 
    def supports_reference_locations(self):
3392
 
        self._ensure_real()
3393
 
        return self._custom_format.supports_reference_locations
3394
 
 
3395
 
    def stores_revno(self):
3396
 
        return True
3397
 
 
3398
3232
    def _use_default_local_heads_to_fetch(self):
3399
3233
        # If the branch format is a metadir format *and* its heads_to_fetch
3400
3234
        # implementation is not overridden vs the base class, we can use the
3402
3236
        # usually cheaper in terms of net round trips, as the last-revision and
3403
3237
        # tags info fetched is cached and would be fetched anyway.
3404
3238
        self._ensure_real()
3405
 
        if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
 
3239
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
3406
3240
            branch_class = self._custom_format._branch_class()
3407
 
            heads_to_fetch_impl = branch_class.heads_to_fetch
3408
 
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch:
 
3241
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
 
3242
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
3409
3243
                return True
3410
3244
        return False
3411
3245
 
3429
3263
        path = self.branch._remote_path()
3430
3264
        try:
3431
3265
            response, handler = self.branch._call_expecting_body(
3432
 
                b'Branch.get_config_file', path)
 
3266
                'Branch.get_config_file', path)
3433
3267
        except errors.UnknownSmartMethod:
3434
3268
            self._ensure_real()
3435
3269
            return self._real_store._load_content()
3436
 
        if len(response) and response[0] != b'ok':
 
3270
        if len(response) and response[0] != 'ok':
3437
3271
            raise errors.UnexpectedSmartServerResponse(response)
3438
3272
        return handler.read_body_bytes()
3439
3273
 
3441
3275
        path = self.branch._remote_path()
3442
3276
        try:
3443
3277
            response, handler = self.branch._call_with_body_bytes_expecting_body(
3444
 
                b'Branch.put_config_file', (path,
3445
 
                                            self.branch._lock_token, self.branch._repo_lock_token),
 
3278
                'Branch.put_config_file', (path,
 
3279
                    self.branch._lock_token, self.branch._repo_lock_token),
3446
3280
                content)
3447
3281
        except errors.UnknownSmartMethod:
3448
3282
            self._ensure_real()
3449
3283
            return self._real_store._save_content(content)
3450
3284
        handler.cancel_read_body()
3451
 
        if response != (b'ok', ):
 
3285
        if response != ('ok', ):
3452
3286
            raise errors.UnexpectedSmartServerResponse(response)
3453
3287
 
3454
3288
    def _ensure_real(self):
3464
3298
    """
3465
3299
 
3466
3300
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
3467
 
                 _client=None, format=None, setup_stacking=True, name=None,
3468
 
                 possible_transports=None):
 
3301
        _client=None, format=None, setup_stacking=True, name=None,
 
3302
        possible_transports=None):
3469
3303
        """Create a RemoteBranch instance.
3470
3304
 
3471
3305
        :param real_branch: An optional local implementation of the branch
3482
3316
        # We intentionally don't call the parent class's __init__, because it
3483
3317
        # will try to assign to self.tags, which is a property in this subclass.
3484
3318
        # And the parent's __init__ doesn't do much anyway.
3485
 
        self.controldir = remote_bzrdir
 
3319
        self.bzrdir = remote_bzrdir
3486
3320
        self.name = name
3487
3321
        if _client is not None:
3488
3322
            self._client = _client
3501
3335
            self._real_branch.repository = self.repository
3502
3336
        else:
3503
3337
            self._real_branch = None
3504
 
        # Fill out expected attributes of branch for breezy API users.
 
3338
        # Fill out expected attributes of branch for brzlib API users.
3505
3339
        self._clear_cached_state()
3506
3340
        # TODO: deprecate self.base in favor of user_url
3507
 
        self.base = self.controldir.user_url
 
3341
        self.base = self.bzrdir.user_url
3508
3342
        self._name = name
3509
3343
        self._control_files = None
3510
3344
        self._lock_mode = None
3545
3379
        # the vfs branch.
3546
3380
        try:
3547
3381
            fallback_url = self.get_stacked_on_url()
3548
 
        except (errors.NotStacked, branch.UnstackableBranchFormat,
3549
 
                errors.UnstackableRepositoryFormat) as e:
 
3382
        except (errors.NotStacked, errors.UnstackableBranchFormat,
 
3383
            errors.UnstackableRepositoryFormat), e:
3550
3384
            return
3551
3385
        self._is_stacked = True
3552
3386
        if possible_transports is None:
3553
3387
            possible_transports = []
3554
3388
        else:
3555
3389
            possible_transports = list(possible_transports)
3556
 
        possible_transports.append(self.controldir.root_transport)
 
3390
        possible_transports.append(self.bzrdir.root_transport)
3557
3391
        self._activate_fallback_location(fallback_url,
3558
 
                                         possible_transports=possible_transports)
 
3392
            possible_transports=possible_transports)
3559
3393
 
3560
3394
    def _get_config(self):
3561
3395
        return RemoteBranchConfig(self)
3562
3396
 
3563
3397
    def _get_config_store(self):
3564
3398
        if self.conf_store is None:
3565
 
            self.conf_store = RemoteBranchStore(self)
 
3399
            self.conf_store =  RemoteBranchStore(self)
3566
3400
        return self.conf_store
3567
3401
 
3568
3402
    def store_uncommitted(self, creator):
3593
3427
        if self._real_branch is None:
3594
3428
            if not vfs.vfs_enabled():
3595
3429
                raise AssertionError('smart server vfs must be enabled '
3596
 
                                     'to use vfs implementation')
3597
 
            self.controldir._ensure_real()
3598
 
            self._real_branch = self.controldir._real_bzrdir.open_branch(
 
3430
                    'to use vfs implementation')
 
3431
            self.bzrdir._ensure_real()
 
3432
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
3599
3433
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
3600
3434
            # The remote branch and the real branch shares the same store. If
3601
3435
            # we don't, there will always be cases where one of the stores
3621
3455
 
3622
3456
    def _clear_cached_state(self):
3623
3457
        super(RemoteBranch, self)._clear_cached_state()
3624
 
        self._tags_bytes = None
3625
3458
        if self._real_branch is not None:
3626
3459
            self._real_branch._clear_cached_state()
3627
3460
 
3643
3476
        # because it triggers an _ensure_real that we otherwise might not need.
3644
3477
        if self._control_files is None:
3645
3478
            self._control_files = RemoteBranchLockableFiles(
3646
 
                self.controldir, self._client)
 
3479
                self.bzrdir, self._client)
3647
3480
        return self._control_files
3648
3481
 
3649
3482
    def get_physical_lock_status(self):
3650
3483
        """See Branch.get_physical_lock_status()."""
3651
3484
        try:
3652
 
            response = self._client.call(b'Branch.get_physical_lock_status',
3653
 
                                         self._remote_path())
 
3485
            response = self._client.call('Branch.get_physical_lock_status',
 
3486
                self._remote_path())
3654
3487
        except errors.UnknownSmartMethod:
3655
3488
            self._ensure_real()
3656
3489
            return self._real_branch.get_physical_lock_status()
3657
 
        if response[0] not in (b'yes', b'no'):
 
3490
        if response[0] not in ('yes', 'no'):
3658
3491
            raise errors.UnexpectedSmartServerResponse(response)
3659
 
        return (response[0] == b'yes')
 
3492
        return (response[0] == 'yes')
3660
3493
 
3661
3494
    def get_stacked_on_url(self):
3662
3495
        """Get the URL this branch is stacked against.
3670
3503
        try:
3671
3504
            # there may not be a repository yet, so we can't use
3672
3505
            # self._translate_error, so we can't use self._call either.
3673
 
            response = self._client.call(b'Branch.get_stacked_on_url',
3674
 
                                         self._remote_path())
3675
 
        except errors.ErrorFromSmartServer as err:
 
3506
            response = self._client.call('Branch.get_stacked_on_url',
 
3507
                self._remote_path())
 
3508
        except errors.ErrorFromSmartServer, err:
3676
3509
            # there may not be a repository yet, so we can't call through
3677
3510
            # its _translate_error
3678
3511
            _translate_error(err, branch=self)
3679
 
        except errors.UnknownSmartMethod as err:
 
3512
        except errors.UnknownSmartMethod, err:
3680
3513
            self._ensure_real()
3681
3514
            return self._real_branch.get_stacked_on_url()
3682
 
        if response[0] != b'ok':
 
3515
        if response[0] != 'ok':
3683
3516
            raise errors.UnexpectedSmartServerResponse(response)
3684
 
        return response[1].decode('utf-8')
 
3517
        return response[1]
3685
3518
 
3686
3519
    def set_stacked_on_url(self, url):
3687
3520
        branch.Branch.set_stacked_on_url(self, url)
3688
3521
        # We need the stacked_on_url to be visible both locally (to not query
3689
3522
        # it repeatedly) and remotely (so smart verbs can get it server side)
3690
3523
        # Without the following line,
3691
 
        # breezy.tests.per_branch.test_create_clone.TestCreateClone
 
3524
        # brzlib.tests.per_branch.test_create_clone.TestCreateClone
3692
3525
        # .test_create_clone_on_transport_stacked_hooks_get_stacked_branch
3693
3526
        # fails for remote branches -- vila 2012-01-04
3694
3527
        self.conf_store.save_changes()
3701
3534
        self._ensure_real()
3702
3535
        return self._real_branch._get_tags_bytes()
3703
3536
 
 
3537
    @needs_read_lock
3704
3538
    def _get_tags_bytes(self):
3705
 
        with self.lock_read():
3706
 
            if self._tags_bytes is None:
3707
 
                self._tags_bytes = self._get_tags_bytes_via_hpss()
3708
 
            return self._tags_bytes
 
3539
        if self._tags_bytes is None:
 
3540
            self._tags_bytes = self._get_tags_bytes_via_hpss()
 
3541
        return self._tags_bytes
3709
3542
 
3710
3543
    def _get_tags_bytes_via_hpss(self):
3711
3544
        medium = self._client._medium
3712
3545
        if medium._is_remote_before((1, 13)):
3713
3546
            return self._vfs_get_tags_bytes()
3714
3547
        try:
3715
 
            response = self._call(
3716
 
                b'Branch.get_tags_bytes', self._remote_path())
 
3548
            response = self._call('Branch.get_tags_bytes', self._remote_path())
3717
3549
        except errors.UnknownSmartMethod:
3718
3550
            medium._remember_remote_is_before((1, 13))
3719
3551
            return self._vfs_get_tags_bytes()
3734
3566
            args = (
3735
3567
                self._remote_path(), self._lock_token, self._repo_lock_token)
3736
3568
            response = self._call_with_body_bytes(
3737
 
                b'Branch.set_tags_bytes', args, bytes)
 
3569
                'Branch.set_tags_bytes', args, bytes)
3738
3570
        except errors.UnknownSmartMethod:
3739
3571
            medium._remember_remote_is_before((1, 18))
3740
3572
            self._vfs_set_tags_bytes(bytes)
3742
3574
    def lock_read(self):
3743
3575
        """Lock the branch for read operations.
3744
3576
 
3745
 
        :return: A breezy.lock.LogicalLockResult.
 
3577
        :return: A brzlib.lock.LogicalLockResult.
3746
3578
        """
3747
3579
        self.repository.lock_read()
3748
3580
        if not self._lock_mode:
3757
3589
 
3758
3590
    def _remote_lock_write(self, token):
3759
3591
        if token is None:
3760
 
            branch_token = repo_token = b''
 
3592
            branch_token = repo_token = ''
3761
3593
        else:
3762
3594
            branch_token = token
3763
3595
            repo_token = self.repository.lock_write().repository_token
3765
3597
        err_context = {'token': token}
3766
3598
        try:
3767
3599
            response = self._call(
3768
 
                b'Branch.lock_write', self._remote_path(), branch_token,
3769
 
                repo_token or b'', **err_context)
3770
 
        except errors.LockContention as e:
 
3600
                'Branch.lock_write', self._remote_path(), branch_token,
 
3601
                repo_token or '', **err_context)
 
3602
        except errors.LockContention, e:
3771
3603
            # The LockContention from the server doesn't have any
3772
3604
            # information about the lock_url. We re-raise LockContention
3773
3605
            # with valid lock_url.
3774
3606
            raise errors.LockContention('(remote lock)',
3775
 
                                        self.repository.base.split('.bzr/')[0])
3776
 
        if response[0] != b'ok':
 
3607
                self.repository.base.split('.bzr/')[0])
 
3608
        if response[0] != 'ok':
3777
3609
            raise errors.UnexpectedSmartServerResponse(response)
3778
3610
        ok, branch_token, repo_token = response
3779
3611
        return branch_token, repo_token
3785
3617
            remote_tokens = self._remote_lock_write(token)
3786
3618
            self._lock_token, self._repo_lock_token = remote_tokens
3787
3619
            if not self._lock_token:
3788
 
                raise SmartProtocolError(
3789
 
                    'Remote server did not return a token!')
 
3620
                raise SmartProtocolError('Remote server did not return a token!')
3790
3621
            # Tell the self.repository object that it is locked.
3791
3622
            self.repository.lock_write(
3792
3623
                self._repo_lock_token, _skip_rpc=True)
3816
3647
    def _unlock(self, branch_token, repo_token):
3817
3648
        err_context = {'token': str((branch_token, repo_token))}
3818
3649
        response = self._call(
3819
 
            b'Branch.unlock', self._remote_path(), branch_token,
3820
 
            repo_token or b'', **err_context)
3821
 
        if response == (b'ok',):
 
3650
            'Branch.unlock', self._remote_path(), branch_token,
 
3651
            repo_token or '', **err_context)
 
3652
        if response == ('ok',):
3822
3653
            return
3823
3654
        raise errors.UnexpectedSmartServerResponse(response)
3824
3655
 
3833
3664
                mode = self._lock_mode
3834
3665
                self._lock_mode = None
3835
3666
                if self._real_branch is not None:
3836
 
                    if (not self._leave_lock and mode == 'w'
3837
 
                            and self._repo_lock_token):
 
3667
                    if (not self._leave_lock and mode == 'w' and
 
3668
                        self._repo_lock_token):
3838
3669
                        # If this RemoteBranch will remove the physical lock
3839
3670
                        # for the repository, make sure the _real_branch
3840
3671
                        # doesn't do it first.  (Because the _real_branch's
3859
3690
    def break_lock(self):
3860
3691
        try:
3861
3692
            response = self._call(
3862
 
                b'Branch.break_lock', self._remote_path())
 
3693
                'Branch.break_lock', self._remote_path())
3863
3694
        except errors.UnknownSmartMethod:
3864
3695
            self._ensure_real()
3865
3696
            return self._real_branch.break_lock()
3866
 
        if response != (b'ok',):
 
3697
        if response != ('ok',):
3867
3698
            raise errors.UnexpectedSmartServerResponse(response)
3868
3699
 
3869
3700
    def leave_lock_in_place(self):
3876
3707
            raise NotImplementedError(self.dont_leave_lock_in_place)
3877
3708
        self._leave_lock = False
3878
3709
 
 
3710
    @needs_read_lock
3879
3711
    def get_rev_id(self, revno, history=None):
3880
3712
        if revno == 0:
3881
3713
            return _mod_revision.NULL_REVISION
3882
 
        with self.lock_read():
3883
 
            last_revision_info = self.last_revision_info()
3884
 
            if revno < 0:
3885
 
                raise errors.RevnoOutOfBounds(
3886
 
                    revno, (0, last_revision_info[0]))
3887
 
            ok, result = self.repository.get_rev_id_for_revno(
3888
 
                revno, last_revision_info)
3889
 
            if ok:
3890
 
                return result
3891
 
            missing_parent = result[1]
3892
 
            # Either the revision named by the server is missing, or its parent
3893
 
            # is.  Call get_parent_map to determine which, so that we report a
3894
 
            # useful error.
3895
 
            parent_map = self.repository.get_parent_map([missing_parent])
3896
 
            if missing_parent in parent_map:
3897
 
                missing_parent = parent_map[missing_parent]
3898
 
            raise errors.NoSuchRevision(self, missing_parent)
 
3714
        last_revision_info = self.last_revision_info()
 
3715
        ok, result = self.repository.get_rev_id_for_revno(
 
3716
            revno, last_revision_info)
 
3717
        if ok:
 
3718
            return result
 
3719
        missing_parent = result[1]
 
3720
        # Either the revision named by the server is missing, or its parent
 
3721
        # is.  Call get_parent_map to determine which, so that we report a
 
3722
        # useful error.
 
3723
        parent_map = self.repository.get_parent_map([missing_parent])
 
3724
        if missing_parent in parent_map:
 
3725
            missing_parent = parent_map[missing_parent]
 
3726
        raise errors.RevisionNotPresent(missing_parent, self.repository)
3899
3727
 
3900
3728
    def _read_last_revision_info(self):
3901
 
        response = self._call(
3902
 
            b'Branch.last_revision_info', self._remote_path())
3903
 
        if response[0] != b'ok':
3904
 
            raise SmartProtocolError(
3905
 
                'unexpected response code %s' % (response,))
 
3729
        response = self._call('Branch.last_revision_info', self._remote_path())
 
3730
        if response[0] != 'ok':
 
3731
            raise SmartProtocolError('unexpected response code %s' % (response,))
3906
3732
        revno = int(response[1])
3907
3733
        last_revision = response[2]
3908
3734
        return (revno, last_revision)
3913
3739
            self._ensure_real()
3914
3740
            return self._real_branch._gen_revision_history()
3915
3741
        response_tuple, response_handler = self._call_expecting_body(
3916
 
            b'Branch.revision_history', self._remote_path())
3917
 
        if response_tuple[0] != b'ok':
 
3742
            'Branch.revision_history', self._remote_path())
 
3743
        if response_tuple[0] != 'ok':
3918
3744
            raise errors.UnexpectedSmartServerResponse(response_tuple)
3919
 
        result = response_handler.read_body_bytes().split(b'\x00')
 
3745
        result = response_handler.read_body_bytes().split('\x00')
3920
3746
        if result == ['']:
3921
3747
            return []
3922
3748
        return result
3923
3749
 
3924
3750
    def _remote_path(self):
3925
 
        return self.controldir._path_for_remote_call(self._client)
 
3751
        return self.bzrdir._path_for_remote_call(self._client)
3926
3752
 
3927
3753
    def _set_last_revision_descendant(self, revision_id, other_branch,
3928
 
                                      allow_diverged=False, allow_overwrite_descendant=False):
 
3754
            allow_diverged=False, allow_overwrite_descendant=False):
3929
3755
        # This performs additional work to meet the hook contract; while its
3930
3756
        # undesirable, we have to synthesise the revno to call the hook, and
3931
3757
        # not calling the hook is worse as it means changes can't be prevented.
3936
3762
        history = self._lefthand_history(revision_id)
3937
3763
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
3938
3764
        err_context = {'other_branch': other_branch}
3939
 
        response = self._call(b'Branch.set_last_revision_ex',
3940
 
                              self._remote_path(), self._lock_token, self._repo_lock_token,
3941
 
                              revision_id, int(allow_diverged), int(
3942
 
                                  allow_overwrite_descendant),
3943
 
                              **err_context)
 
3765
        response = self._call('Branch.set_last_revision_ex',
 
3766
            self._remote_path(), self._lock_token, self._repo_lock_token,
 
3767
            revision_id, int(allow_diverged), int(allow_overwrite_descendant),
 
3768
            **err_context)
3944
3769
        self._clear_cached_state()
3945
 
        if len(response) != 3 and response[0] != b'ok':
 
3770
        if len(response) != 3 and response[0] != 'ok':
3946
3771
            raise errors.UnexpectedSmartServerResponse(response)
3947
3772
        new_revno, new_revision_id = response[1:]
3948
3773
        self._last_revision_info_cache = new_revno, new_revision_id
3962
3787
        history = self._lefthand_history(revision_id)
3963
3788
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
3964
3789
        self._clear_cached_state()
3965
 
        response = self._call(b'Branch.set_last_revision',
3966
 
                              self._remote_path(), self._lock_token, self._repo_lock_token,
3967
 
                              revision_id)
3968
 
        if response != (b'ok',):
 
3790
        response = self._call('Branch.set_last_revision',
 
3791
            self._remote_path(), self._lock_token, self._repo_lock_token,
 
3792
            revision_id)
 
3793
        if response != ('ok',):
3969
3794
            raise errors.UnexpectedSmartServerResponse(response)
3970
3795
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
3971
3796
 
3974
3799
        if medium._is_remote_before((1, 13)):
3975
3800
            return self._vfs_get_parent_location()
3976
3801
        try:
3977
 
            response = self._call(b'Branch.get_parent', self._remote_path())
 
3802
            response = self._call('Branch.get_parent', self._remote_path())
3978
3803
        except errors.UnknownSmartMethod:
3979
3804
            medium._remember_remote_is_before((1, 13))
3980
3805
            return self._vfs_get_parent_location()
3981
3806
        if len(response) != 1:
3982
3807
            raise errors.UnexpectedSmartServerResponse(response)
3983
3808
        parent_location = response[0]
3984
 
        if parent_location == b'':
 
3809
        if parent_location == '':
3985
3810
            return None
3986
 
        return parent_location.decode('utf-8')
 
3811
        return parent_location
3987
3812
 
3988
3813
    def _vfs_get_parent_location(self):
3989
3814
        self._ensure_real()
3994
3819
        if medium._is_remote_before((1, 15)):
3995
3820
            return self._vfs_set_parent_location(url)
3996
3821
        try:
3997
 
            call_url = url or u''
3998
 
            if isinstance(call_url, str):
3999
 
                call_url = call_url.encode('utf-8')
4000
 
            response = self._call(b'Branch.set_parent_location',
4001
 
                                  self._remote_path(), self._lock_token, self._repo_lock_token,
4002
 
                                  call_url)
 
3822
            call_url = url or ''
 
3823
            if type(call_url) is not str:
 
3824
                raise AssertionError('url must be a str or None (%s)' % url)
 
3825
            response = self._call('Branch.set_parent_location',
 
3826
                self._remote_path(), self._lock_token, self._repo_lock_token,
 
3827
                call_url)
4003
3828
        except errors.UnknownSmartMethod:
4004
3829
            medium._remember_remote_is_before((1, 15))
4005
3830
            return self._vfs_set_parent_location(url)
4010
3835
        self._ensure_real()
4011
3836
        return self._real_branch._set_parent_location(url)
4012
3837
 
 
3838
    @needs_write_lock
4013
3839
    def pull(self, source, overwrite=False, stop_revision=None,
4014
3840
             **kwargs):
4015
 
        with self.lock_write():
4016
 
            self._clear_cached_state_of_remote_branch_only()
4017
 
            self._ensure_real()
4018
 
            return self._real_branch.pull(
4019
 
                source, overwrite=overwrite, stop_revision=stop_revision,
4020
 
                _override_hook_target=self, **kwargs)
 
3841
        self._clear_cached_state_of_remote_branch_only()
 
3842
        self._ensure_real()
 
3843
        return self._real_branch.pull(
 
3844
            source, overwrite=overwrite, stop_revision=stop_revision,
 
3845
            _override_hook_target=self, **kwargs)
4021
3846
 
 
3847
    @needs_read_lock
4022
3848
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
4023
 
        with self.lock_read():
4024
 
            self._ensure_real()
4025
 
            return self._real_branch.push(
4026
 
                target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4027
 
                _override_hook_source_branch=self)
 
3849
        self._ensure_real()
 
3850
        return self._real_branch.push(
 
3851
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
 
3852
            _override_hook_source_branch=self)
4028
3853
 
4029
3854
    def peek_lock_mode(self):
4030
3855
        return self._lock_mode
4032
3857
    def is_locked(self):
4033
3858
        return self._lock_count >= 1
4034
3859
 
 
3860
    @needs_read_lock
4035
3861
    def revision_id_to_dotted_revno(self, revision_id):
4036
3862
        """Given a revision id, return its dotted revno.
4037
3863
 
4038
3864
        :return: a tuple like (1,) or (400,1,3).
4039
3865
        """
4040
 
        with self.lock_read():
4041
 
            try:
4042
 
                response = self._call(b'Branch.revision_id_to_revno',
4043
 
                                      self._remote_path(), revision_id)
4044
 
            except errors.UnknownSmartMethod:
4045
 
                self._ensure_real()
4046
 
                return self._real_branch.revision_id_to_dotted_revno(revision_id)
4047
 
            except errors.UnknownErrorFromSmartServer as e:
4048
 
                # Deal with older versions of bzr/brz that didn't explicitly
4049
 
                # wrap GhostRevisionsHaveNoRevno.
4050
 
                if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4051
 
                    (revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4052
 
                    raise errors.GhostRevisionsHaveNoRevno(
4053
 
                        revid, ghost_revid)
4054
 
                raise
4055
 
            if response[0] == b'ok':
4056
 
                return tuple([int(x) for x in response[1:]])
4057
 
            else:
4058
 
                raise errors.UnexpectedSmartServerResponse(response)
 
3866
        try:
 
3867
            response = self._call('Branch.revision_id_to_revno',
 
3868
                self._remote_path(), revision_id)
 
3869
        except errors.UnknownSmartMethod:
 
3870
            self._ensure_real()
 
3871
            return self._real_branch.revision_id_to_dotted_revno(revision_id)
 
3872
        if response[0] == 'ok':
 
3873
            return tuple([int(x) for x in response[1:]])
 
3874
        else:
 
3875
            raise errors.UnexpectedSmartServerResponse(response)
4059
3876
 
 
3877
    @needs_read_lock
4060
3878
    def revision_id_to_revno(self, revision_id):
4061
3879
        """Given a revision id on the branch mainline, return its revno.
4062
3880
 
4063
3881
        :return: an integer
4064
3882
        """
4065
 
        with self.lock_read():
4066
 
            try:
4067
 
                response = self._call(b'Branch.revision_id_to_revno',
4068
 
                                      self._remote_path(), revision_id)
4069
 
            except errors.UnknownSmartMethod:
4070
 
                self._ensure_real()
4071
 
                return self._real_branch.revision_id_to_revno(revision_id)
4072
 
            if response[0] == b'ok':
4073
 
                if len(response) == 2:
4074
 
                    return int(response[1])
4075
 
                raise NoSuchRevision(self, revision_id)
4076
 
            else:
4077
 
                raise errors.UnexpectedSmartServerResponse(response)
 
3883
        try:
 
3884
            response = self._call('Branch.revision_id_to_revno',
 
3885
                self._remote_path(), revision_id)
 
3886
        except errors.UnknownSmartMethod:
 
3887
            self._ensure_real()
 
3888
            return self._real_branch.revision_id_to_revno(revision_id)
 
3889
        if response[0] == 'ok':
 
3890
            if len(response) == 2:
 
3891
                return int(response[1])
 
3892
            raise NoSuchRevision(self, revision_id)
 
3893
        else:
 
3894
            raise errors.UnexpectedSmartServerResponse(response)
4078
3895
 
 
3896
    @needs_write_lock
4079
3897
    def set_last_revision_info(self, revno, revision_id):
4080
 
        with self.lock_write():
4081
 
            # XXX: These should be returned by the set_last_revision_info verb
4082
 
            old_revno, old_revid = self.last_revision_info()
4083
 
            self._run_pre_change_branch_tip_hooks(revno, revision_id)
4084
 
            if not revision_id or not isinstance(revision_id, bytes):
4085
 
                raise errors.InvalidRevisionId(
4086
 
                    revision_id=revision_id, branch=self)
4087
 
            try:
4088
 
                response = self._call(b'Branch.set_last_revision_info',
4089
 
                                      self._remote_path(), self._lock_token, self._repo_lock_token,
4090
 
                                      str(revno).encode('ascii'), revision_id)
4091
 
            except errors.UnknownSmartMethod:
4092
 
                self._ensure_real()
4093
 
                self._clear_cached_state_of_remote_branch_only()
4094
 
                self._real_branch.set_last_revision_info(revno, revision_id)
4095
 
                self._last_revision_info_cache = revno, revision_id
4096
 
                return
4097
 
            if response == (b'ok',):
4098
 
                self._clear_cached_state()
4099
 
                self._last_revision_info_cache = revno, revision_id
4100
 
                self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4101
 
                # Update the _real_branch's cache too.
4102
 
                if self._real_branch is not None:
4103
 
                    cache = self._last_revision_info_cache
4104
 
                    self._real_branch._last_revision_info_cache = cache
4105
 
            else:
4106
 
                raise errors.UnexpectedSmartServerResponse(response)
 
3898
        # XXX: These should be returned by the set_last_revision_info verb
 
3899
        old_revno, old_revid = self.last_revision_info()
 
3900
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
3901
        if not revision_id or not isinstance(revision_id, basestring):
 
3902
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
3903
        try:
 
3904
            response = self._call('Branch.set_last_revision_info',
 
3905
                self._remote_path(), self._lock_token, self._repo_lock_token,
 
3906
                str(revno), revision_id)
 
3907
        except errors.UnknownSmartMethod:
 
3908
            self._ensure_real()
 
3909
            self._clear_cached_state_of_remote_branch_only()
 
3910
            self._real_branch.set_last_revision_info(revno, revision_id)
 
3911
            self._last_revision_info_cache = revno, revision_id
 
3912
            return
 
3913
        if response == ('ok',):
 
3914
            self._clear_cached_state()
 
3915
            self._last_revision_info_cache = revno, revision_id
 
3916
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
3917
            # Update the _real_branch's cache too.
 
3918
            if self._real_branch is not None:
 
3919
                cache = self._last_revision_info_cache
 
3920
                self._real_branch._last_revision_info_cache = cache
 
3921
        else:
 
3922
            raise errors.UnexpectedSmartServerResponse(response)
4107
3923
 
 
3924
    @needs_write_lock
4108
3925
    def generate_revision_history(self, revision_id, last_rev=None,
4109
3926
                                  other_branch=None):
4110
 
        with self.lock_write():
4111
 
            medium = self._client._medium
4112
 
            if not medium._is_remote_before((1, 6)):
4113
 
                # Use a smart method for 1.6 and above servers
4114
 
                try:
4115
 
                    self._set_last_revision_descendant(revision_id, other_branch,
4116
 
                                                       allow_diverged=True, allow_overwrite_descendant=True)
4117
 
                    return
4118
 
                except errors.UnknownSmartMethod:
4119
 
                    medium._remember_remote_is_before((1, 6))
4120
 
            self._clear_cached_state_of_remote_branch_only()
4121
 
            graph = self.repository.get_graph()
4122
 
            (last_revno, last_revid) = self.last_revision_info()
4123
 
            known_revision_ids = [
4124
 
                (last_revid, last_revno),
4125
 
                (_mod_revision.NULL_REVISION, 0),
4126
 
                ]
4127
 
            if last_rev is not None:
4128
 
                if not graph.is_ancestor(last_rev, revision_id):
4129
 
                    # our previous tip is not merged into stop_revision
4130
 
                    raise errors.DivergedBranches(self, other_branch)
4131
 
            revno = graph.find_distance_to_null(
4132
 
                revision_id, known_revision_ids)
4133
 
            self.set_last_revision_info(revno, revision_id)
 
3927
        medium = self._client._medium
 
3928
        if not medium._is_remote_before((1, 6)):
 
3929
            # Use a smart method for 1.6 and above servers
 
3930
            try:
 
3931
                self._set_last_revision_descendant(revision_id, other_branch,
 
3932
                    allow_diverged=True, allow_overwrite_descendant=True)
 
3933
                return
 
3934
            except errors.UnknownSmartMethod:
 
3935
                medium._remember_remote_is_before((1, 6))
 
3936
        self._clear_cached_state_of_remote_branch_only()
 
3937
        graph = self.repository.get_graph()
 
3938
        (last_revno, last_revid) = self.last_revision_info()
 
3939
        known_revision_ids = [
 
3940
            (last_revid, last_revno),
 
3941
            (_mod_revision.NULL_REVISION, 0),
 
3942
            ]
 
3943
        if last_rev is not None:
 
3944
            if not graph.is_ancestor(last_rev, revision_id):
 
3945
                # our previous tip is not merged into stop_revision
 
3946
                raise errors.DivergedBranches(self, other_branch)
 
3947
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
 
3948
        self.set_last_revision_info(revno, revision_id)
4134
3949
 
4135
3950
    def set_push_location(self, location):
4136
3951
        self._set_config_location('push_location', location)
4152
3967
            return self._vfs_heads_to_fetch()
4153
3968
 
4154
3969
    def _rpc_heads_to_fetch(self):
4155
 
        response = self._call(b'Branch.heads_to_fetch', self._remote_path())
 
3970
        response = self._call('Branch.heads_to_fetch', self._remote_path())
4156
3971
        if len(response) != 2:
4157
3972
            raise errors.UnexpectedSmartServerResponse(response)
4158
3973
        must_fetch, if_present_fetch = response
4162
3977
        self._ensure_real()
4163
3978
        return self._real_branch.heads_to_fetch()
4164
3979
 
4165
 
    def reconcile(self, thorough=True):
4166
 
        """Make sure the data stored in this branch is consistent."""
4167
 
        from .reconcile import BranchReconciler
4168
 
        with self.lock_write():
4169
 
            reconciler = BranchReconciler(self, thorough=thorough)
4170
 
            return reconciler.reconcile()
4171
 
 
4172
 
    def get_reference_info(self, file_id):
4173
 
        """Get the tree_path and branch_location for a tree reference."""
4174
 
        if not self._format.supports_reference_locations:
4175
 
            raise errors.UnsupportedOperation(self.get_reference_info, self)
4176
 
        return self._get_all_reference_info().get(file_id, (None, None))
4177
 
 
4178
 
    def set_reference_info(self, file_id, branch_location, tree_path=None):
4179
 
        """Set the branch location to use for a tree reference."""
4180
 
        if not self._format.supports_reference_locations:
4181
 
            raise errors.UnsupportedOperation(self.set_reference_info, self)
4182
 
        self._ensure_real()
4183
 
        self._real_branch.set_reference_info(
4184
 
            file_id, branch_location, tree_path)
4185
 
 
4186
 
    def _set_all_reference_info(self, reference_info):
4187
 
        if not self._format.supports_reference_locations:
4188
 
            raise errors.UnsupportedOperation(self.set_reference_info, self)
4189
 
        self._ensure_real()
4190
 
        self._real_branch._set_all_reference_info(reference_info)
4191
 
 
4192
 
    def _get_all_reference_info(self):
4193
 
        if not self._format.supports_reference_locations:
4194
 
            return {}
4195
 
        try:
4196
 
            response, handler = self._call_expecting_body(
4197
 
                b'Branch.get_all_reference_info', self._remote_path())
4198
 
        except errors.UnknownSmartMethod:
4199
 
            self._ensure_real()
4200
 
            return self._real_branch._get_all_reference_info()
4201
 
        if len(response) and response[0] != b'ok':
4202
 
            raise errors.UnexpectedSmartServerResponse(response)
4203
 
        ret = {}
4204
 
        for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4205
 
            ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4206
 
        return ret
4207
 
 
4208
 
    def reference_parent(self, file_id, path, possible_transports=None):
4209
 
        """Return the parent branch for a tree-reference.
4210
 
 
4211
 
        :param path: The path of the nested tree in the tree
4212
 
        :return: A branch associated with the nested tree
4213
 
        """
4214
 
        branch_location = self.get_reference_info(file_id)[0]
4215
 
        if branch_location is None:
4216
 
            try:
4217
 
                return branch.Branch.open_from_transport(
4218
 
                    self.controldir.root_transport.clone(path),
4219
 
                    possible_transports=possible_transports)
4220
 
            except errors.NotBranchError:
4221
 
                return None
4222
 
        return branch.Branch.open(
4223
 
            urlutils.join(
4224
 
                urlutils.strip_segment_parameters(self.user_url), branch_location),
4225
 
            possible_transports=possible_transports)
4226
 
 
4227
3980
 
4228
3981
class RemoteConfig(object):
4229
3982
    """A Config that reads and writes from smart verbs.
4230
3983
 
4231
3984
    It is a low-level object that considers config data to be name/value pairs
4232
3985
    that may be associated with a section. Assigning meaning to the these
4233
 
    values is done at higher levels like breezy.config.TreeConfig.
 
3986
    values is done at higher levels like brzlib.config.TreeConfig.
4234
3987
    """
4235
3988
 
4236
3989
    def get_option(self, name, section=None, default=None):
4262
4015
        return value
4263
4016
 
4264
4017
    def _response_to_configobj(self, response):
4265
 
        if len(response[0]) and response[0][0] != b'ok':
 
4018
        if len(response[0]) and response[0][0] != 'ok':
4266
4019
            raise errors.UnexpectedSmartServerResponse(response)
4267
4020
        lines = response[1].read_body_bytes().splitlines()
4268
4021
        conf = _mod_config.ConfigObj(lines, encoding='utf-8')
4280
4033
    def _get_configobj(self):
4281
4034
        path = self._branch._remote_path()
4282
4035
        response = self._branch._client.call_expecting_body(
4283
 
            b'Branch.get_config_file', path)
 
4036
            'Branch.get_config_file', path)
4284
4037
        return self._response_to_configobj(response)
4285
4038
 
4286
4039
    def set_option(self, value, name, section=None):
4301
4054
            return self._set_config_option(value, name, section)
4302
4055
 
4303
4056
    def _set_config_option(self, value, name, section):
4304
 
        if isinstance(value, (bool, int)):
4305
 
            value = str(value)
4306
 
        elif isinstance(value, str):
4307
 
            pass
4308
 
        else:
4309
 
            raise TypeError(value)
4310
4057
        try:
4311
4058
            path = self._branch._remote_path()
4312
 
            response = self._branch._client.call(b'Branch.set_config_option',
4313
 
                                                 path, self._branch._lock_token, self._branch._repo_lock_token,
4314
 
                                                 value.encode('utf-8'), name.encode('utf-8'),
4315
 
                                                 (section or '').encode('utf-8'))
 
4059
            response = self._branch._client.call('Branch.set_config_option',
 
4060
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
4061
                value.encode('utf8'), name, section or '')
4316
4062
        except errors.UnknownSmartMethod:
4317
4063
            medium = self._branch._client._medium
4318
4064
            medium._remember_remote_is_before((1, 14))
4323
4069
    def _serialize_option_dict(self, option_dict):
4324
4070
        utf8_dict = {}
4325
4071
        for key, value in option_dict.items():
4326
 
            if isinstance(key, str):
 
4072
            if isinstance(key, unicode):
4327
4073
                key = key.encode('utf8')
4328
 
            if isinstance(value, str):
 
4074
            if isinstance(value, unicode):
4329
4075
                value = value.encode('utf8')
4330
4076
            utf8_dict[key] = value
4331
4077
        return bencode.bencode(utf8_dict)
4335
4081
            path = self._branch._remote_path()
4336
4082
            serialised_dict = self._serialize_option_dict(value)
4337
4083
            response = self._branch._client.call(
4338
 
                b'Branch.set_config_option_dict',
 
4084
                'Branch.set_config_option_dict',
4339
4085
                path, self._branch._lock_token, self._branch._repo_lock_token,
4340
 
                serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
 
4086
                serialised_dict, name, section or '')
4341
4087
        except errors.UnknownSmartMethod:
4342
4088
            medium = self._branch._client._medium
4343
4089
            medium._remember_remote_is_before((2, 2))
4362
4108
 
4363
4109
    def _get_configobj(self):
4364
4110
        medium = self._bzrdir._client._medium
4365
 
        verb = b'BzrDir.get_config_file'
 
4111
        verb = 'BzrDir.get_config_file'
4366
4112
        if medium._is_remote_before((1, 15)):
4367
4113
            raise errors.UnknownSmartMethod(verb)
4368
4114
        path = self._bzrdir._path_for_remote_call(self._bzrdir._client)
4389
4135
        return self._bzrdir._real_bzrdir
4390
4136
 
4391
4137
 
 
4138
def _extract_tar(tar, to_dir):
 
4139
    """Extract all the contents of a tarfile object.
 
4140
 
 
4141
    A replacement for extractall, which is not present in python2.4
 
4142
    """
 
4143
    for tarinfo in tar:
 
4144
        tar.extract(tarinfo, to_dir)
 
4145
 
 
4146
 
4392
4147
error_translators = registry.Registry()
4393
4148
no_context_error_translators = registry.Registry()
4394
4149
 
4410
4165
    def find(name):
4411
4166
        try:
4412
4167
            return context[name]
4413
 
        except KeyError:
4414
 
            mutter('Missing key \'%s\' in context %r', name, context)
 
4168
        except KeyError, key_err:
 
4169
            mutter('Missing key %r in context %r', key_err.args[0], context)
4415
4170
            raise err
4416
 
 
4417
4171
    def get_path():
4418
4172
        """Get the path from the context if present, otherwise use first error
4419
4173
        arg.
4420
4174
        """
4421
4175
        try:
4422
4176
            return context['path']
4423
 
        except KeyError:
 
4177
        except KeyError, key_err:
4424
4178
            try:
4425
 
                return err.error_args[0].decode('utf-8')
4426
 
            except IndexError:
4427
 
                mutter('Missing key \'path\' in context %r', context)
 
4179
                return err.error_args[0]
 
4180
            except IndexError, idx_err:
 
4181
                mutter(
 
4182
                    'Missing key %r in context %r', key_err.args[0], context)
4428
4183
                raise err
4429
 
    if not isinstance(err.error_verb, bytes):
4430
 
        raise TypeError(err.error_verb)
 
4184
 
4431
4185
    try:
4432
4186
        translator = error_translators.get(err.error_verb)
4433
4187
    except KeyError:
4442
4196
        raise translator(err)
4443
4197
 
4444
4198
 
4445
 
error_translators.register(b'NoSuchRevision',
4446
 
                           lambda err, find, get_path: NoSuchRevision(
4447
 
                               find('branch'), err.error_args[0]))
4448
 
error_translators.register(b'nosuchrevision',
4449
 
                           lambda err, find, get_path: NoSuchRevision(
4450
 
                               find('repository'), err.error_args[0]))
4451
 
error_translators.register(
4452
 
    b'revno-outofbounds',
4453
 
    lambda err, find, get_path: errors.RevnoOutOfBounds(
4454
 
        err.error_args[0], (err.error_args[1], err.error_args[2])))
4455
 
 
 
4199
error_translators.register('NoSuchRevision',
 
4200
    lambda err, find, get_path: NoSuchRevision(
 
4201
        find('branch'), err.error_args[0]))
 
4202
error_translators.register('nosuchrevision',
 
4203
    lambda err, find, get_path: NoSuchRevision(
 
4204
        find('repository'), err.error_args[0]))
4456
4205
 
4457
4206
def _translate_nobranch_error(err, find, get_path):
4458
4207
    if len(err.error_args) >= 1:
4459
 
        extra = err.error_args[0].decode('utf-8')
 
4208
        extra = err.error_args[0]
4460
4209
    else:
4461
4210
        extra = None
4462
4211
    return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4463
 
                                 detail=extra)
4464
 
 
4465
 
 
4466
 
error_translators.register(b'nobranch', _translate_nobranch_error)
4467
 
error_translators.register(b'norepository',
4468
 
                           lambda err, find, get_path: errors.NoRepositoryPresent(
4469
 
                               find('bzrdir')))
4470
 
error_translators.register(b'UnlockableTransport',
4471
 
                           lambda err, find, get_path: errors.UnlockableTransport(
4472
 
                               find('bzrdir').root_transport))
4473
 
error_translators.register(b'TokenMismatch',
4474
 
                           lambda err, find, get_path: errors.TokenMismatch(
4475
 
                               find('token'), '(remote token)'))
4476
 
error_translators.register(b'Diverged',
4477
 
                           lambda err, find, get_path: errors.DivergedBranches(
4478
 
                               find('branch'), find('other_branch')))
4479
 
error_translators.register(b'NotStacked',
4480
 
                           lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4481
 
 
 
4212
        detail=extra)
 
4213
 
 
4214
error_translators.register('nobranch', _translate_nobranch_error)
 
4215
error_translators.register('norepository',
 
4216
    lambda err, find, get_path: errors.NoRepositoryPresent(
 
4217
        find('bzrdir')))
 
4218
error_translators.register('UnlockableTransport',
 
4219
    lambda err, find, get_path: errors.UnlockableTransport(
 
4220
        find('bzrdir').root_transport))
 
4221
error_translators.register('TokenMismatch',
 
4222
    lambda err, find, get_path: errors.TokenMismatch(
 
4223
        find('token'), '(remote token)'))
 
4224
error_translators.register('Diverged',
 
4225
    lambda err, find, get_path: errors.DivergedBranches(
 
4226
        find('branch'), find('other_branch')))
 
4227
error_translators.register('NotStacked',
 
4228
    lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4482
4229
 
4483
4230
def _translate_PermissionDenied(err, find, get_path):
4484
4231
    path = get_path()
4485
4232
    if len(err.error_args) >= 2:
4486
 
        extra = err.error_args[1].decode('utf-8')
 
4233
        extra = err.error_args[1]
4487
4234
    else:
4488
4235
        extra = None
4489
4236
    return errors.PermissionDenied(path, extra=extra)
4490
4237
 
4491
 
 
4492
 
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4493
 
error_translators.register(b'ReadError',
4494
 
                           lambda err, find, get_path: errors.ReadError(get_path()))
4495
 
error_translators.register(b'NoSuchFile',
4496
 
                           lambda err, find, get_path: errors.NoSuchFile(get_path()))
4497
 
error_translators.register(b'TokenLockingNotSupported',
4498
 
                           lambda err, find, get_path: errors.TokenLockingNotSupported(
4499
 
                               find('repository')))
4500
 
error_translators.register(b'UnsuspendableWriteGroup',
4501
 
                           lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4502
 
                               repository=find('repository')))
4503
 
error_translators.register(b'UnresumableWriteGroup',
4504
 
                           lambda err, find, get_path: errors.UnresumableWriteGroup(
4505
 
                               repository=find('repository'), write_groups=err.error_args[0],
4506
 
                               reason=err.error_args[1]))
4507
 
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4508
 
                                      lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4509
 
no_context_error_translators.register(b'IncompatibleRepositories',
4510
 
                                      lambda err: errors.IncompatibleRepositories(
4511
 
                                          err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4512
 
no_context_error_translators.register(b'LockContention',
4513
 
                                      lambda err: errors.LockContention('(remote lock)'))
4514
 
no_context_error_translators.register(b'LockFailed',
4515
 
                                      lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4516
 
no_context_error_translators.register(b'TipChangeRejected',
4517
 
                                      lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4518
 
no_context_error_translators.register(b'UnstackableBranchFormat',
4519
 
                                      lambda err: branch.UnstackableBranchFormat(*err.error_args))
4520
 
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4521
 
                                      lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4522
 
no_context_error_translators.register(b'FileExists',
4523
 
                                      lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4524
 
no_context_error_translators.register(b'DirectoryNotEmpty',
4525
 
                                      lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4526
 
no_context_error_translators.register(b'UnknownFormat',
4527
 
                                      lambda err: errors.UnknownFormatError(
4528
 
                                          err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4529
 
no_context_error_translators.register(b'InvalidURL',
4530
 
                                      lambda err: urlutils.InvalidURL(
4531
 
                                          err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4532
 
 
 
4238
error_translators.register('PermissionDenied', _translate_PermissionDenied)
 
4239
error_translators.register('ReadError',
 
4240
    lambda err, find, get_path: errors.ReadError(get_path()))
 
4241
error_translators.register('NoSuchFile',
 
4242
    lambda err, find, get_path: errors.NoSuchFile(get_path()))
 
4243
error_translators.register('TokenLockingNotSupported',
 
4244
    lambda err, find, get_path: errors.TokenLockingNotSupported(
 
4245
        find('repository')))
 
4246
error_translators.register('UnsuspendableWriteGroup',
 
4247
    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
 
4248
        repository=find('repository')))
 
4249
error_translators.register('UnresumableWriteGroup',
 
4250
    lambda err, find, get_path: errors.UnresumableWriteGroup(
 
4251
        repository=find('repository'), write_groups=err.error_args[0],
 
4252
        reason=err.error_args[1]))
 
4253
no_context_error_translators.register('IncompatibleRepositories',
 
4254
    lambda err: errors.IncompatibleRepositories(
 
4255
        err.error_args[0], err.error_args[1], err.error_args[2]))
 
4256
no_context_error_translators.register('LockContention',
 
4257
    lambda err: errors.LockContention('(remote lock)'))
 
4258
no_context_error_translators.register('LockFailed',
 
4259
    lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
 
4260
no_context_error_translators.register('TipChangeRejected',
 
4261
    lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
 
4262
no_context_error_translators.register('UnstackableBranchFormat',
 
4263
    lambda err: errors.UnstackableBranchFormat(*err.error_args))
 
4264
no_context_error_translators.register('UnstackableRepositoryFormat',
 
4265
    lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
 
4266
no_context_error_translators.register('FileExists',
 
4267
    lambda err: errors.FileExists(err.error_args[0]))
 
4268
no_context_error_translators.register('DirectoryNotEmpty',
 
4269
    lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
4533
4270
 
4534
4271
def _translate_short_readv_error(err):
4535
4272
    args = err.error_args
4536
 
    return errors.ShortReadvError(
4537
 
        args[0].decode('utf-8'),
4538
 
        int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4539
 
        int(args[3].decode('ascii')))
4540
 
 
4541
 
 
4542
 
no_context_error_translators.register(b'ShortReadvError',
4543
 
                                      _translate_short_readv_error)
4544
 
 
 
4273
    return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
 
4274
        int(args[3]))
 
4275
 
 
4276
no_context_error_translators.register('ShortReadvError',
 
4277
    _translate_short_readv_error)
4545
4278
 
4546
4279
def _translate_unicode_error(err):
4547
 
    encoding = err.error_args[0].decode('ascii')
4548
 
    val = err.error_args[1].decode('utf-8')
4549
 
    start = int(err.error_args[2].decode('ascii'))
4550
 
    end = int(err.error_args[3].decode('ascii'))
4551
 
    reason = err.error_args[4].decode('utf-8')
4552
 
    if val.startswith('u:'):
4553
 
        val = val[2:].decode('utf-8')
4554
 
    elif val.startswith('s:'):
4555
 
        val = val[2:].decode('base64')
4556
 
    if err.error_verb == 'UnicodeDecodeError':
4557
 
        raise UnicodeDecodeError(encoding, val, start, end, reason)
4558
 
    elif err.error_verb == 'UnicodeEncodeError':
4559
 
        raise UnicodeEncodeError(encoding, val, start, end, reason)
4560
 
 
4561
 
 
4562
 
no_context_error_translators.register(b'UnicodeEncodeError',
4563
 
                                      _translate_unicode_error)
4564
 
no_context_error_translators.register(b'UnicodeDecodeError',
4565
 
                                      _translate_unicode_error)
4566
 
no_context_error_translators.register(b'ReadOnlyError',
4567
 
                                      lambda err: errors.TransportNotPossible('readonly transport'))
4568
 
no_context_error_translators.register(b'MemoryError',
4569
 
                                      lambda err: errors.BzrError("remote server out of memory\n"
4570
 
                                                                  "Retry non-remotely, or contact the server admin for details."))
4571
 
no_context_error_translators.register(b'RevisionNotPresent',
4572
 
                                      lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4573
 
 
4574
 
no_context_error_translators.register(b'BzrCheckError',
4575
 
                                      lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))
 
4280
        encoding = str(err.error_args[0]) # encoding must always be a string
 
4281
        val = err.error_args[1]
 
4282
        start = int(err.error_args[2])
 
4283
        end = int(err.error_args[3])
 
4284
        reason = str(err.error_args[4]) # reason must always be a string
 
4285
        if val.startswith('u:'):
 
4286
            val = val[2:].decode('utf-8')
 
4287
        elif val.startswith('s:'):
 
4288
            val = val[2:].decode('base64')
 
4289
        if err.error_verb == 'UnicodeDecodeError':
 
4290
            raise UnicodeDecodeError(encoding, val, start, end, reason)
 
4291
        elif err.error_verb == 'UnicodeEncodeError':
 
4292
            raise UnicodeEncodeError(encoding, val, start, end, reason)
 
4293
 
 
4294
no_context_error_translators.register('UnicodeEncodeError',
 
4295
    _translate_unicode_error)
 
4296
no_context_error_translators.register('UnicodeDecodeError',
 
4297
    _translate_unicode_error)
 
4298
no_context_error_translators.register('ReadOnlyError',
 
4299
    lambda err: errors.TransportNotPossible('readonly transport'))
 
4300
no_context_error_translators.register('MemoryError',
 
4301
    lambda err: errors.BzrError("remote server out of memory\n"
 
4302
        "Retry non-remotely, or contact the server admin for details."))
 
4303
no_context_error_translators.register('RevisionNotPresent',
 
4304
    lambda err: errors.RevisionNotPresent(err.error_args[0], err.error_args[1]))
 
4305
 
 
4306
no_context_error_translators.register('BzrCheckError',
 
4307
    lambda err: errors.BzrCheckError(msg=err.error_args[0]))
 
4308