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

  • Committer: Jelmer Vernooij
  • Date: 2017-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

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