/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Robert Collins
  • Date: 2010-05-05 00:05:29 UTC
  • mto: This revision was merged to the branch mainline in revision 5206.
  • Revision ID: robertc@robertcollins.net-20100505000529-ltmllyms5watqj5u
Make 'pydoc bzrlib.tests.build_tree_shape' useful.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2012 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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
 
 
19
17
import bz2
20
 
import os
21
 
import re
22
 
import sys
23
 
import zlib
24
18
 
25
 
from .. import (
 
19
from bzrlib import (
26
20
    bencode,
27
21
    branch,
28
 
    bzr as _mod_bzr,
29
 
    config as _mod_config,
30
 
    controldir,
 
22
    bzrdir,
 
23
    config,
31
24
    debug,
32
25
    errors,
33
 
    gpg,
34
26
    graph,
35
27
    lock,
36
28
    lockdir,
37
 
    osutils,
38
 
    registry,
 
29
    repository,
39
30
    repository as _mod_repository,
 
31
    revision,
40
32
    revision as _mod_revision,
41
 
    urlutils,
42
 
    )
43
 
from . import (
44
 
    branch as bzrbranch,
45
 
    bzrdir as _mod_bzrdir,
46
 
    inventory_delta,
47
 
    testament as _mod_testament,
48
 
    vf_repository,
49
 
    vf_search,
50
 
    )
51
 
from .branch import BranchReferenceFormat
52
 
from ..branch import BranchWriteLockResult
53
 
from ..decorators import only_raises
54
 
from ..errors import (
 
33
    static_tuple,
 
34
    symbol_versioning,
 
35
)
 
36
from bzrlib.branch import BranchReferenceFormat
 
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
 
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
39
from bzrlib.errors import (
55
40
    NoSuchRevision,
56
41
    SmartProtocolError,
57
42
    )
58
 
from ..i18n import gettext
59
 
from .inventory import Inventory
60
 
from .inventorytree import InventoryRevisionTree
61
 
from ..lockable_files import LockableFiles
62
 
from ..sixish import (
63
 
    get_unbound_function,
64
 
    map,
65
 
    text_type,
66
 
    viewitems,
67
 
    viewvalues,
68
 
    )
69
 
from .smart import client, vfs, repository as smart_repo
70
 
from .smart.client import _SmartClient
71
 
from ..revision import NULL_REVISION
72
 
from ..repository import RepositoryWriteLockResult, _LazyListJoin
73
 
from .serializer import format_registry as serializer_format_registry
74
 
from ..trace import mutter, note, warning, log_exception_quietly
75
 
from .versionedfile import FulltextContentFactory
76
 
 
77
 
 
78
 
_DEFAULT_SEARCH_DEPTH = 100
 
43
from bzrlib.lockable_files import LockableFiles
 
44
from bzrlib.smart import client, vfs, repository as smart_repo
 
45
from bzrlib.revision import ensure_null, NULL_REVISION
 
46
from bzrlib.trace import mutter, note, warning
79
47
 
80
48
 
81
49
class _RpcHelper(object):
84
52
    def _call(self, method, *args, **err_context):
85
53
        try:
86
54
            return self._client.call(method, *args)
87
 
        except errors.ErrorFromSmartServer as err:
 
55
        except errors.ErrorFromSmartServer, err:
88
56
            self._translate_error(err, **err_context)
89
57
 
90
58
    def _call_expecting_body(self, method, *args, **err_context):
91
59
        try:
92
60
            return self._client.call_expecting_body(method, *args)
93
 
        except errors.ErrorFromSmartServer as err:
 
61
        except errors.ErrorFromSmartServer, err:
94
62
            self._translate_error(err, **err_context)
95
63
 
96
64
    def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
97
65
        try:
98
66
            return self._client.call_with_body_bytes(method, args, body_bytes)
99
 
        except errors.ErrorFromSmartServer as err:
 
67
        except errors.ErrorFromSmartServer, err:
100
68
            self._translate_error(err, **err_context)
101
69
 
102
70
    def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
104
72
        try:
105
73
            return self._client.call_with_body_bytes_expecting_body(
106
74
                method, args, body_bytes)
107
 
        except errors.ErrorFromSmartServer as err:
 
75
        except errors.ErrorFromSmartServer, err:
108
76
            self._translate_error(err, **err_context)
109
77
 
110
78
 
111
79
def response_tuple_to_repo_format(response):
112
80
    """Convert a response tuple describing a repository format to a format."""
113
81
    format = RemoteRepositoryFormat()
114
 
    format._rich_root_data = (response[0] == b'yes')
115
 
    format._supports_tree_reference = (response[1] == b'yes')
116
 
    format._supports_external_lookups = (response[2] == b'yes')
 
82
    format._rich_root_data = (response[0] == 'yes')
 
83
    format._supports_tree_reference = (response[1] == 'yes')
 
84
    format._supports_external_lookups = (response[2] == 'yes')
117
85
    format._network_name = response[3]
118
86
    return format
119
87
 
120
88
 
121
 
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
122
 
# does not have to be imported unless a remote format is involved.
123
 
 
124
 
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
125
 
    """Format representing bzrdirs accessed via a smart server"""
126
 
 
127
 
    supports_workingtrees = False
128
 
 
129
 
    colocated_branches = False
130
 
 
131
 
    def __init__(self):
132
 
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
133
 
        # XXX: It's a bit ugly that the network name is here, because we'd
134
 
        # like to believe that format objects are stateless or at least
135
 
        # immutable,  However, we do at least avoid mutating the name after
136
 
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
137
 
        self._network_name = None
138
 
 
139
 
    def __repr__(self):
140
 
        return "%s(_network_name=%r)" % (self.__class__.__name__,
141
 
                                         self._network_name)
142
 
 
143
 
    def get_format_description(self):
144
 
        if self._network_name:
145
 
            try:
146
 
                real_format = controldir.network_format_registry.get(
147
 
                    self._network_name)
148
 
            except KeyError:
149
 
                pass
150
 
            else:
151
 
                return 'Remote: ' + real_format.get_format_description()
152
 
        return 'bzr remote bzrdir'
153
 
 
154
 
    def get_format_string(self):
155
 
        raise NotImplementedError(self.get_format_string)
156
 
 
157
 
    def network_name(self):
158
 
        if self._network_name:
159
 
            return self._network_name
160
 
        else:
161
 
            raise AssertionError("No network name set.")
162
 
 
163
 
    def initialize_on_transport(self, transport):
164
 
        try:
165
 
            # hand off the request to the smart server
166
 
            client_medium = transport.get_smart_medium()
167
 
        except errors.NoSmartMedium:
168
 
            # TODO: lookup the local format from a server hint.
169
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
170
 
            return local_dir_format.initialize_on_transport(transport)
171
 
        client = _SmartClient(client_medium)
172
 
        path = client.remote_path_from_transport(transport)
173
 
        try:
174
 
            response = client.call(b'BzrDirFormat.initialize', path)
175
 
        except errors.ErrorFromSmartServer as err:
176
 
            _translate_error(err, path=path)
177
 
        if response[0] != b'ok':
178
 
            raise errors.SmartProtocolError(
179
 
                'unexpected response code %s' % (response,))
180
 
        format = RemoteBzrDirFormat()
181
 
        self._supply_sub_formats_to(format)
182
 
        return RemoteBzrDir(transport, format)
183
 
 
184
 
    def parse_NoneTrueFalse(self, arg):
185
 
        if not arg:
186
 
            return None
187
 
        if arg == b'False':
188
 
            return False
189
 
        if arg == b'True':
190
 
            return True
191
 
        raise AssertionError("invalid arg %r" % arg)
192
 
 
193
 
    def _serialize_NoneTrueFalse(self, arg):
194
 
        if arg is False:
195
 
            return b'False'
196
 
        if arg:
197
 
            return b'True'
198
 
        return b''
199
 
 
200
 
    def _serialize_NoneString(self, arg):
201
 
        return arg or b''
202
 
 
203
 
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
204
 
                                   create_prefix=False, force_new_repo=False, stacked_on=None,
205
 
                                   stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
206
 
                                   shared_repo=False):
207
 
        try:
208
 
            # hand off the request to the smart server
209
 
            client_medium = transport.get_smart_medium()
210
 
        except errors.NoSmartMedium:
211
 
            do_vfs = True
212
 
        else:
213
 
            # Decline to open it if the server doesn't support our required
214
 
            # version (3) so that the VFS-based transport will do it.
215
 
            if client_medium.should_probe():
216
 
                try:
217
 
                    server_version = client_medium.protocol_version()
218
 
                    if server_version != '2':
219
 
                        do_vfs = True
220
 
                    else:
221
 
                        do_vfs = False
222
 
                except errors.SmartProtocolError:
223
 
                    # Apparently there's no usable smart server there, even though
224
 
                    # the medium supports the smart protocol.
225
 
                    do_vfs = True
226
 
            else:
227
 
                do_vfs = False
228
 
        if not do_vfs:
229
 
            client = _SmartClient(client_medium)
230
 
            path = client.remote_path_from_transport(transport)
231
 
            if client_medium._is_remote_before((1, 16)):
232
 
                do_vfs = True
233
 
        if do_vfs:
234
 
            # TODO: lookup the local format from a server hint.
235
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
236
 
            self._supply_sub_formats_to(local_dir_format)
237
 
            return local_dir_format.initialize_on_transport_ex(transport,
238
 
                                                               use_existing_dir=use_existing_dir, create_prefix=create_prefix,
239
 
                                                               force_new_repo=force_new_repo, stacked_on=stacked_on,
240
 
                                                               stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
241
 
                                                               make_working_trees=make_working_trees, shared_repo=shared_repo,
242
 
                                                               vfs_only=True)
243
 
        return self._initialize_on_transport_ex_rpc(client, path, transport,
244
 
                                                    use_existing_dir, create_prefix, force_new_repo, stacked_on,
245
 
                                                    stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
246
 
 
247
 
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
248
 
                                        use_existing_dir, create_prefix, force_new_repo, stacked_on,
249
 
                                        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
250
 
        args = []
251
 
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
252
 
        args.append(self._serialize_NoneTrueFalse(create_prefix))
253
 
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
254
 
        args.append(self._serialize_NoneString(stacked_on))
255
 
        # stack_on_pwd is often/usually our transport
256
 
        if stack_on_pwd:
257
 
            try:
258
 
                stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
259
 
                if not stack_on_pwd:
260
 
                    stack_on_pwd = b'.'
261
 
            except errors.PathNotChild:
262
 
                pass
263
 
        args.append(self._serialize_NoneString(stack_on_pwd))
264
 
        args.append(self._serialize_NoneString(repo_format_name))
265
 
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
266
 
        args.append(self._serialize_NoneTrueFalse(shared_repo))
267
 
        request_network_name = self._network_name or \
268
 
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
269
 
        try:
270
 
            response = client.call(b'BzrDirFormat.initialize_ex_1.16',
271
 
                                   request_network_name, path, *args)
272
 
        except errors.UnknownSmartMethod:
273
 
            client._medium._remember_remote_is_before((1, 16))
274
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
275
 
            self._supply_sub_formats_to(local_dir_format)
276
 
            return local_dir_format.initialize_on_transport_ex(transport,
277
 
                                                               use_existing_dir=use_existing_dir, create_prefix=create_prefix,
278
 
                                                               force_new_repo=force_new_repo, stacked_on=stacked_on,
279
 
                                                               stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
280
 
                                                               make_working_trees=make_working_trees, shared_repo=shared_repo,
281
 
                                                               vfs_only=True)
282
 
        except errors.ErrorFromSmartServer as err:
283
 
            _translate_error(err, path=path.decode('utf-8'))
284
 
        repo_path = response[0]
285
 
        bzrdir_name = response[6]
286
 
        require_stacking = response[7]
287
 
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
288
 
        format = RemoteBzrDirFormat()
289
 
        format._network_name = bzrdir_name
290
 
        self._supply_sub_formats_to(format)
291
 
        bzrdir = RemoteBzrDir(transport, format, _client=client)
292
 
        if repo_path:
293
 
            repo_format = response_tuple_to_repo_format(response[1:])
294
 
            if repo_path == b'.':
295
 
                repo_path = b''
296
 
            repo_path = repo_path.decode('utf-8')
297
 
            if repo_path:
298
 
                repo_bzrdir_format = RemoteBzrDirFormat()
299
 
                repo_bzrdir_format._network_name = response[5]
300
 
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
301
 
                                        repo_bzrdir_format)
302
 
            else:
303
 
                repo_bzr = bzrdir
304
 
            final_stack = response[8] or None
305
 
            if final_stack:
306
 
                final_stack = final_stack.decode('utf-8')
307
 
            final_stack_pwd = response[9] or None
308
 
            if final_stack_pwd:
309
 
                final_stack_pwd = urlutils.join(
310
 
                    transport.base, final_stack_pwd.decode('utf-8'))
311
 
            remote_repo = RemoteRepository(repo_bzr, repo_format)
312
 
            if len(response) > 10:
313
 
                # Updated server verb that locks remotely.
314
 
                repo_lock_token = response[10] or None
315
 
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
316
 
                if repo_lock_token:
317
 
                    remote_repo.dont_leave_lock_in_place()
318
 
            else:
319
 
                remote_repo.lock_write()
320
 
            policy = _mod_bzrdir.UseExistingRepository(remote_repo,
321
 
                                                       final_stack, final_stack_pwd, require_stacking)
322
 
            policy.acquire_repository()
323
 
        else:
324
 
            remote_repo = None
325
 
            policy = None
326
 
        bzrdir._format.set_branch_format(self.get_branch_format())
327
 
        if require_stacking:
328
 
            # The repo has already been created, but we need to make sure that
329
 
            # we'll make a stackable branch.
330
 
            bzrdir._format.require_stacking(_skip_repo=True)
331
 
        return remote_repo, bzrdir, require_stacking, policy
332
 
 
333
 
    def _open(self, transport):
334
 
        return RemoteBzrDir(transport, self)
335
 
 
336
 
    def __eq__(self, other):
337
 
        if not isinstance(other, RemoteBzrDirFormat):
338
 
            return False
339
 
        return self.get_format_description() == other.get_format_description()
340
 
 
341
 
    def __return_repository_format(self):
342
 
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
343
 
        # repository format has been asked for, tell the RemoteRepositoryFormat
344
 
        # that it should use that for init() etc.
345
 
        result = RemoteRepositoryFormat()
346
 
        custom_format = getattr(self, '_repository_format', None)
347
 
        if custom_format:
348
 
            if isinstance(custom_format, RemoteRepositoryFormat):
349
 
                return custom_format
350
 
            else:
351
 
                # We will use the custom format to create repositories over the
352
 
                # wire; expose its details like rich_root_data for code to
353
 
                # query
354
 
                result._custom_format = custom_format
355
 
        return result
356
 
 
357
 
    def get_branch_format(self):
358
 
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
359
 
        if not isinstance(result, RemoteBranchFormat):
360
 
            new_result = RemoteBranchFormat()
361
 
            new_result._custom_format = result
362
 
            # cache the result
363
 
            self.set_branch_format(new_result)
364
 
            result = new_result
365
 
        return result
366
 
 
367
 
    repository_format = property(__return_repository_format,
368
 
                                 _mod_bzrdir.BzrDirMetaFormat1._set_repository_format)  # .im_func)
369
 
 
370
 
 
371
 
class RemoteControlStore(_mod_config.IniFileStore):
372
 
    """Control store which attempts to use HPSS calls to retrieve control store.
373
 
 
374
 
    Note that this is specific to bzr-based formats.
375
 
    """
376
 
 
377
 
    def __init__(self, bzrdir):
378
 
        super(RemoteControlStore, self).__init__()
379
 
        self.controldir = bzrdir
380
 
        self._real_store = None
381
 
 
382
 
    def lock_write(self, token=None):
383
 
        self._ensure_real()
384
 
        return self._real_store.lock_write(token)
385
 
 
386
 
    def unlock(self):
387
 
        self._ensure_real()
388
 
        return self._real_store.unlock()
389
 
 
390
 
    def save(self):
391
 
        with self.lock_write():
392
 
            # We need to be able to override the undecorated implementation
393
 
            self.save_without_locking()
394
 
 
395
 
    def save_without_locking(self):
396
 
        super(RemoteControlStore, self).save()
397
 
 
398
 
    def _ensure_real(self):
399
 
        self.controldir._ensure_real()
400
 
        if self._real_store is None:
401
 
            self._real_store = _mod_config.ControlStore(self.controldir)
402
 
 
403
 
    def external_url(self):
404
 
        return urlutils.join(self.branch.user_url, 'control.conf')
405
 
 
406
 
    def _load_content(self):
407
 
        medium = self.controldir._client._medium
408
 
        path = self.controldir._path_for_remote_call(self.controldir._client)
409
 
        try:
410
 
            response, handler = self.controldir._call_expecting_body(
411
 
                b'BzrDir.get_config_file', path)
412
 
        except errors.UnknownSmartMethod:
413
 
            self._ensure_real()
414
 
            return self._real_store._load_content()
415
 
        if len(response) and response[0] != b'ok':
416
 
            raise errors.UnexpectedSmartServerResponse(response)
417
 
        return handler.read_body_bytes()
418
 
 
419
 
    def _save_content(self, content):
420
 
        # FIXME JRV 2011-11-22: Ideally this should use a
421
 
        # HPSS call too, but at the moment it is not possible
422
 
        # to write lock control directories.
423
 
        self._ensure_real()
424
 
        return self._real_store._save_content(content)
425
 
 
426
 
 
427
 
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
 
89
# Note: RemoteBzrDirFormat is in bzrdir.py
 
90
 
 
91
class RemoteBzrDir(BzrDir, _RpcHelper):
428
92
    """Control directory on a remote server, accessed via bzr:// or similar."""
429
93
 
430
94
    def __init__(self, transport, format, _client=None, _force_probe=False):
433
97
        :param _client: Private parameter for testing. Disables probing and the
434
98
            use of a real bzrdir.
435
99
        """
436
 
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
 
100
        BzrDir.__init__(self, transport, format)
437
101
        # this object holds a delegated bzrdir that uses file-level operations
438
102
        # to talk to the other side
439
103
        self._real_bzrdir = None
469
133
            self._rpc_open(path)
470
134
 
471
135
    def _rpc_open_2_1(self, path):
472
 
        response = self._call(b'BzrDir.open_2.1', path)
473
 
        if response == (b'no',):
 
136
        response = self._call('BzrDir.open_2.1', path)
 
137
        if response == ('no',):
474
138
            raise errors.NotBranchError(path=self.root_transport.base)
475
 
        elif response[0] == b'yes':
476
 
            if response[1] == b'yes':
 
139
        elif response[0] == 'yes':
 
140
            if response[1] == 'yes':
477
141
                self._has_working_tree = True
478
 
            elif response[1] == b'no':
 
142
            elif response[1] == 'no':
479
143
                self._has_working_tree = False
480
144
            else:
481
145
                raise errors.UnexpectedSmartServerResponse(response)
483
147
            raise errors.UnexpectedSmartServerResponse(response)
484
148
 
485
149
    def _rpc_open(self, path):
486
 
        response = self._call(b'BzrDir.open', path)
487
 
        if response not in [(b'yes',), (b'no',)]:
 
150
        response = self._call('BzrDir.open', path)
 
151
        if response not in [('yes',), ('no',)]:
488
152
            raise errors.UnexpectedSmartServerResponse(response)
489
 
        if response == (b'no',):
 
153
        if response == ('no',):
490
154
            raise errors.NotBranchError(path=self.root_transport.base)
491
155
 
492
156
    def _ensure_real(self):
498
162
            if 'hpssvfs' in debug.debug_flags:
499
163
                import traceback
500
164
                warning('VFS BzrDir access triggered\n%s',
501
 
                        ''.join(traceback.format_stack()))
502
 
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
503
 
                self.root_transport, probers=[_mod_bzr.BzrProber])
 
165
                    ''.join(traceback.format_stack()))
 
166
            self._real_bzrdir = BzrDir.open_from_transport(
 
167
                self.root_transport, _server_formats=False)
504
168
            self._format._network_name = \
505
169
                self._real_bzrdir._format.network_name()
506
170
 
511
175
        # Prevent aliasing problems in the next_open_branch_result cache.
512
176
        # See create_branch for rationale.
513
177
        self._next_open_branch_result = None
514
 
        return _mod_bzrdir.BzrDir.break_lock(self)
515
 
 
516
 
    def _vfs_checkout_metadir(self):
517
 
        self._ensure_real()
518
 
        return self._real_bzrdir.checkout_metadir()
519
 
 
520
 
    def checkout_metadir(self):
521
 
        """Retrieve the controldir format to use for checkouts of this one.
522
 
        """
523
 
        medium = self._client._medium
524
 
        if medium._is_remote_before((2, 5)):
525
 
            return self._vfs_checkout_metadir()
526
 
        path = self._path_for_remote_call(self._client)
527
 
        try:
528
 
            response = self._client.call(b'BzrDir.checkout_metadir',
529
 
                                         path)
530
 
        except errors.UnknownSmartMethod:
531
 
            medium._remember_remote_is_before((2, 5))
532
 
            return self._vfs_checkout_metadir()
533
 
        if len(response) != 3:
534
 
            raise errors.UnexpectedSmartServerResponse(response)
535
 
        control_name, repo_name, branch_name = response
536
 
        try:
537
 
            format = controldir.network_format_registry.get(control_name)
538
 
        except KeyError:
539
 
            raise errors.UnknownFormatError(kind='control',
540
 
                                            format=control_name)
541
 
        if repo_name:
542
 
            try:
543
 
                repo_format = _mod_repository.network_format_registry.get(
544
 
                    repo_name)
545
 
            except KeyError:
546
 
                raise errors.UnknownFormatError(kind='repository',
547
 
                                                format=repo_name)
548
 
            format.repository_format = repo_format
549
 
        if branch_name:
550
 
            try:
551
 
                format.set_branch_format(
552
 
                    branch.network_format_registry.get(branch_name))
553
 
            except KeyError:
554
 
                raise errors.UnknownFormatError(kind='branch',
555
 
                                                format=branch_name)
556
 
        return format
 
178
        return BzrDir.break_lock(self)
557
179
 
558
180
    def _vfs_cloning_metadir(self, require_stacking=False):
559
181
        self._ensure_real()
564
186
        medium = self._client._medium
565
187
        if medium._is_remote_before((1, 13)):
566
188
            return self._vfs_cloning_metadir(require_stacking=require_stacking)
567
 
        verb = b'BzrDir.cloning_metadir'
 
189
        verb = 'BzrDir.cloning_metadir'
568
190
        if require_stacking:
569
 
            stacking = b'True'
 
191
            stacking = 'True'
570
192
        else:
571
 
            stacking = b'False'
 
193
            stacking = 'False'
572
194
        path = self._path_for_remote_call(self._client)
573
195
        try:
574
196
            response = self._call(verb, path, stacking)
575
197
        except errors.UnknownSmartMethod:
576
198
            medium._remember_remote_is_before((1, 13))
577
199
            return self._vfs_cloning_metadir(require_stacking=require_stacking)
578
 
        except errors.UnknownErrorFromSmartServer as err:
579
 
            if err.error_tuple != (b'BranchReference',):
 
200
        except errors.UnknownErrorFromSmartServer, err:
 
201
            if err.error_tuple != ('BranchReference',):
580
202
                raise
581
203
            # We need to resolve the branch reference to determine the
582
204
            # cloning_metadir.  This causes unnecessary RPCs to open the
583
205
            # referenced branch (and bzrdir, etc) but only when the caller
584
206
            # didn't already resolve the branch reference.
585
207
            referenced_branch = self.open_branch()
586
 
            return referenced_branch.controldir.cloning_metadir()
 
208
            return referenced_branch.bzrdir.cloning_metadir()
587
209
        if len(response) != 3:
588
210
            raise errors.UnexpectedSmartServerResponse(response)
589
211
        control_name, repo_name, branch_info = response
590
212
        if len(branch_info) != 2:
591
213
            raise errors.UnexpectedSmartServerResponse(response)
592
214
        branch_ref, branch_name = branch_info
593
 
        try:
594
 
            format = controldir.network_format_registry.get(control_name)
595
 
        except KeyError:
596
 
            raise errors.UnknownFormatError(
597
 
                kind='control', format=control_name)
598
 
 
 
215
        format = bzrdir.network_format_registry.get(control_name)
599
216
        if repo_name:
600
 
            try:
601
 
                format.repository_format = _mod_repository.network_format_registry.get(
602
 
                    repo_name)
603
 
            except KeyError:
604
 
                raise errors.UnknownFormatError(kind='repository',
605
 
                                                format=repo_name)
606
 
        if branch_ref == b'ref':
 
217
            format.repository_format = repository.network_format_registry.get(
 
218
                repo_name)
 
219
        if branch_ref == 'ref':
607
220
            # XXX: we need possible_transports here to avoid reopening the
608
221
            # connection to the referenced location
609
 
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
 
222
            ref_bzrdir = BzrDir.open(branch_name)
610
223
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
611
224
            format.set_branch_format(branch_format)
612
 
        elif branch_ref == b'branch':
 
225
        elif branch_ref == 'branch':
613
226
            if branch_name:
614
 
                try:
615
 
                    branch_format = branch.network_format_registry.get(
616
 
                        branch_name)
617
 
                except KeyError:
618
 
                    raise errors.UnknownFormatError(kind='branch',
619
 
                                                    format=branch_name)
620
 
                format.set_branch_format(branch_format)
 
227
                format.set_branch_format(
 
228
                    branch.network_format_registry.get(branch_name))
621
229
        else:
622
230
            raise errors.UnexpectedSmartServerResponse(response)
623
231
        return format
633
241
 
634
242
    def destroy_repository(self):
635
243
        """See BzrDir.destroy_repository"""
636
 
        path = self._path_for_remote_call(self._client)
637
 
        try:
638
 
            response = self._call(b'BzrDir.destroy_repository', path)
639
 
        except errors.UnknownSmartMethod:
640
 
            self._ensure_real()
641
 
            self._real_bzrdir.destroy_repository()
642
 
            return
643
 
        if response[0] != b'ok':
644
 
            raise SmartProtocolError(
645
 
                'unexpected response code %s' % (response,))
 
244
        self._ensure_real()
 
245
        self._real_bzrdir.destroy_repository()
646
246
 
647
 
    def create_branch(self, name=None, repository=None,
648
 
                      append_revisions_only=None):
649
 
        if name is None:
650
 
            name = self._get_selected_branch()
651
 
        if name != "":
652
 
            raise errors.NoColocatedBranchSupport(self)
 
247
    def create_branch(self, name=None):
653
248
        # as per meta1 formats - just delegate to the format object which may
654
249
        # be parameterised.
655
250
        real_branch = self._format.get_branch_format().initialize(self,
656
 
                                                                  name=name, repository=repository,
657
 
                                                                  append_revisions_only=append_revisions_only)
 
251
            name=name)
658
252
        if not isinstance(real_branch, RemoteBranch):
659
 
            if not isinstance(repository, RemoteRepository):
660
 
                raise AssertionError(
661
 
                    'need a RemoteRepository to use with RemoteBranch, got %r'
662
 
                    % (repository,))
663
 
            result = RemoteBranch(self, repository, real_branch, name=name)
 
253
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
254
                                  name=name)
664
255
        else:
665
256
            result = real_branch
666
257
        # BzrDir.clone_on_transport() uses the result of create_branch but does
674
265
 
675
266
    def destroy_branch(self, name=None):
676
267
        """See BzrDir.destroy_branch"""
677
 
        if name is None:
678
 
            name = self._get_selected_branch()
679
 
        if name != "":
680
 
            raise errors.NoColocatedBranchSupport(self)
681
 
        path = self._path_for_remote_call(self._client)
682
 
        try:
683
 
            if name != "":
684
 
                args = (name, )
685
 
            else:
686
 
                args = ()
687
 
            response = self._call(b'BzrDir.destroy_branch', path, *args)
688
 
        except errors.UnknownSmartMethod:
689
 
            self._ensure_real()
690
 
            self._real_bzrdir.destroy_branch(name=name)
691
 
            self._next_open_branch_result = None
692
 
            return
 
268
        self._ensure_real()
 
269
        self._real_bzrdir.destroy_branch(name=name)
693
270
        self._next_open_branch_result = None
694
 
        if response[0] != b'ok':
695
 
            raise SmartProtocolError(
696
 
                'unexpected response code %s' % (response,))
697
271
 
698
 
    def create_workingtree(self, revision_id=None, from_branch=None,
699
 
                           accelerator_tree=None, hardlink=False):
 
272
    def create_workingtree(self, revision_id=None, from_branch=None):
700
273
        raise errors.NotLocalUrl(self.transport.base)
701
274
 
702
 
    def find_branch_format(self, name=None):
 
275
    def find_branch_format(self):
703
276
        """Find the branch 'format' for this bzrdir.
704
277
 
705
278
        This might be a synthetic object for e.g. RemoteBranch and SVN.
706
279
        """
707
 
        b = self.open_branch(name=name)
 
280
        b = self.open_branch()
708
281
        return b._format
709
282
 
710
 
    def branch_names(self):
711
 
        path = self._path_for_remote_call(self._client)
712
 
        try:
713
 
            response, handler = self._call_expecting_body(
714
 
                b'BzrDir.get_branches', path)
715
 
        except errors.UnknownSmartMethod:
716
 
            self._ensure_real()
717
 
            return self._real_bzrdir.branch_names()
718
 
        if response[0] != b"success":
719
 
            raise errors.UnexpectedSmartServerResponse(response)
720
 
        body = bencode.bdecode(handler.read_body_bytes())
721
 
        ret = []
722
 
        for name, value in viewitems(body):
723
 
            name = name.decode('utf-8')
724
 
            ret.append(name)
725
 
        return ret
726
 
 
727
 
    def get_branches(self, possible_transports=None, ignore_fallbacks=False):
728
 
        path = self._path_for_remote_call(self._client)
729
 
        try:
730
 
            response, handler = self._call_expecting_body(
731
 
                b'BzrDir.get_branches', path)
732
 
        except errors.UnknownSmartMethod:
733
 
            self._ensure_real()
734
 
            return self._real_bzrdir.get_branches()
735
 
        if response[0] != b"success":
736
 
            raise errors.UnexpectedSmartServerResponse(response)
737
 
        body = bencode.bdecode(handler.read_body_bytes())
738
 
        ret = {}
739
 
        for name, value in viewitems(body):
740
 
            name = name.decode('utf-8')
741
 
            ret[name] = self._open_branch(
742
 
                name, value[0].decode('ascii'), value[1],
743
 
                possible_transports=possible_transports,
744
 
                ignore_fallbacks=ignore_fallbacks)
745
 
        return ret
746
 
 
747
 
    def set_branch_reference(self, target_branch, name=None):
748
 
        """See BzrDir.set_branch_reference()."""
749
 
        if name is None:
750
 
            name = self._get_selected_branch()
751
 
        if name != "":
752
 
            raise errors.NoColocatedBranchSupport(self)
753
 
        self._ensure_real()
754
 
        return self._real_bzrdir.set_branch_reference(target_branch, name=name)
755
 
 
756
 
    def get_branch_reference(self, name=None):
 
283
    def get_branch_reference(self):
757
284
        """See BzrDir.get_branch_reference()."""
758
 
        if name is None:
759
 
            name = self._get_selected_branch()
760
 
        if name != "":
761
 
            raise errors.NoColocatedBranchSupport(self)
762
285
        response = self._get_branch_reference()
763
286
        if response[0] == 'ref':
764
 
            return response[1].decode('utf-8')
 
287
            return response[1]
765
288
        else:
766
289
            return None
767
290
 
768
291
    def _get_branch_reference(self):
769
 
        """Get branch reference information
770
 
 
771
 
        :return: Tuple with (kind, location_or_format)
772
 
            if kind == 'ref', then location_or_format contains a location
773
 
            otherwise, it contains a format name
774
 
        """
775
292
        path = self._path_for_remote_call(self._client)
776
293
        medium = self._client._medium
777
294
        candidate_calls = [
778
 
            (b'BzrDir.open_branchV3', (2, 1)),
779
 
            (b'BzrDir.open_branchV2', (1, 13)),
780
 
            (b'BzrDir.open_branch', None),
 
295
            ('BzrDir.open_branchV3', (2, 1)),
 
296
            ('BzrDir.open_branchV2', (1, 13)),
 
297
            ('BzrDir.open_branch', None),
781
298
            ]
782
299
        for verb, required_version in candidate_calls:
783
300
            if required_version and medium._is_remote_before(required_version):
790
307
                medium._remember_remote_is_before(required_version)
791
308
            else:
792
309
                break
793
 
        if verb == b'BzrDir.open_branch':
794
 
            if response[0] != b'ok':
 
310
        if verb == 'BzrDir.open_branch':
 
311
            if response[0] != 'ok':
795
312
                raise errors.UnexpectedSmartServerResponse(response)
796
 
            if response[1] != b'':
 
313
            if response[1] != '':
797
314
                return ('ref', response[1])
798
315
            else:
799
 
                return ('branch', b'')
800
 
        if response[0] not in (b'ref', b'branch'):
 
316
                return ('branch', '')
 
317
        if response[0] not in ('ref', 'branch'):
801
318
            raise errors.UnexpectedSmartServerResponse(response)
802
 
        return (response[0].decode('ascii'), response[1])
 
319
        return response
803
320
 
804
 
    def _get_tree_branch(self, name=None):
 
321
    def _get_tree_branch(self):
805
322
        """See BzrDir._get_tree_branch()."""
806
 
        return None, self.open_branch(name=name)
 
323
        return None, self.open_branch()
807
324
 
808
 
    def _open_branch(self, name, kind, location_or_format,
809
 
                     ignore_fallbacks=False, possible_transports=None):
810
 
        if kind == 'ref':
 
325
    def open_branch(self, name=None, unsupported=False,
 
326
                    ignore_fallbacks=False):
 
327
        if unsupported:
 
328
            raise NotImplementedError('unsupported flag support not implemented yet.')
 
329
        if self._next_open_branch_result is not None:
 
330
            # See create_branch for details.
 
331
            result = self._next_open_branch_result
 
332
            self._next_open_branch_result = None
 
333
            return result
 
334
        response = self._get_branch_reference()
 
335
        if response[0] == 'ref':
811
336
            # a branch reference, use the existing BranchReference logic.
812
337
            format = BranchReferenceFormat()
813
 
            ref_loc = urlutils.join(self.user_url, location_or_format.decode('utf-8'))
814
338
            return format.open(self, name=name, _found=True,
815
 
                               location=ref_loc,
816
 
                               ignore_fallbacks=ignore_fallbacks,
817
 
                               possible_transports=possible_transports)
818
 
        branch_format_name = location_or_format
 
339
                location=response[1], ignore_fallbacks=ignore_fallbacks)
 
340
        branch_format_name = response[1]
819
341
        if not branch_format_name:
820
342
            branch_format_name = None
821
343
        format = RemoteBranchFormat(network_name=branch_format_name)
822
344
        return RemoteBranch(self, self.find_repository(), format=format,
823
 
                            setup_stacking=not ignore_fallbacks, name=name,
824
 
                            possible_transports=possible_transports)
825
 
 
826
 
    def open_branch(self, name=None, unsupported=False,
827
 
                    ignore_fallbacks=False, possible_transports=None):
828
 
        if name is None:
829
 
            name = self._get_selected_branch()
830
 
        if name != "":
831
 
            raise errors.NoColocatedBranchSupport(self)
832
 
        if unsupported:
833
 
            raise NotImplementedError(
834
 
                'unsupported flag support not implemented yet.')
835
 
        if self._next_open_branch_result is not None:
836
 
            # See create_branch for details.
837
 
            result = self._next_open_branch_result
838
 
            self._next_open_branch_result = None
839
 
            return result
840
 
        response = self._get_branch_reference()
841
 
        return self._open_branch(name, response[0], response[1],
842
 
                                 possible_transports=possible_transports,
843
 
                                 ignore_fallbacks=ignore_fallbacks)
 
345
            setup_stacking=not ignore_fallbacks, name=name)
844
346
 
845
347
    def _open_repo_v1(self, path):
846
 
        verb = b'BzrDir.find_repository'
 
348
        verb = 'BzrDir.find_repository'
847
349
        response = self._call(verb, path)
848
 
        if response[0] != b'ok':
 
350
        if response[0] != 'ok':
849
351
            raise errors.UnexpectedSmartServerResponse(response)
850
352
        # servers that only support the v1 method don't support external
851
353
        # references either.
852
354
        self._ensure_real()
853
355
        repo = self._real_bzrdir.open_repository()
854
 
        response = response + (b'no', repo._format.network_name())
 
356
        response = response + ('no', repo._format.network_name())
855
357
        return response, repo
856
358
 
857
359
    def _open_repo_v2(self, path):
858
 
        verb = b'BzrDir.find_repositoryV2'
 
360
        verb = 'BzrDir.find_repositoryV2'
859
361
        response = self._call(verb, path)
860
 
        if response[0] != b'ok':
 
362
        if response[0] != 'ok':
861
363
            raise errors.UnexpectedSmartServerResponse(response)
862
364
        self._ensure_real()
863
365
        repo = self._real_bzrdir.open_repository()
865
367
        return response, repo
866
368
 
867
369
    def _open_repo_v3(self, path):
868
 
        verb = b'BzrDir.find_repositoryV3'
 
370
        verb = 'BzrDir.find_repositoryV3'
869
371
        medium = self._client._medium
870
372
        if medium._is_remote_before((1, 13)):
871
373
            raise errors.UnknownSmartMethod(verb)
874
376
        except errors.UnknownSmartMethod:
875
377
            medium._remember_remote_is_before((1, 13))
876
378
            raise
877
 
        if response[0] != b'ok':
 
379
        if response[0] != 'ok':
878
380
            raise errors.UnexpectedSmartServerResponse(response)
879
381
        return response, None
880
382
 
882
384
        path = self._path_for_remote_call(self._client)
883
385
        response = None
884
386
        for probe in [self._open_repo_v3, self._open_repo_v2,
885
 
                      self._open_repo_v1]:
 
387
            self._open_repo_v1]:
886
388
            try:
887
389
                response, real_repo = probe(path)
888
390
                break
889
391
            except errors.UnknownSmartMethod:
890
392
                pass
891
393
        if response is None:
892
 
            raise errors.UnknownSmartMethod(b'BzrDir.find_repository{3,2,}')
893
 
        if response[0] != b'ok':
 
394
            raise errors.UnknownSmartMethod('BzrDir.find_repository{3,2,}')
 
395
        if response[0] != 'ok':
894
396
            raise errors.UnexpectedSmartServerResponse(response)
895
397
        if len(response) != 6:
896
 
            raise SmartProtocolError(
897
 
                'incorrect response length %s' % (response,))
898
 
        if response[1] == b'':
 
398
            raise SmartProtocolError('incorrect response length %s' % (response,))
 
399
        if response[1] == '':
899
400
            # repo is at this dir.
900
401
            format = response_tuple_to_repo_format(response[2:])
901
402
            # Used to support creating a real format instance when needed.
910
411
 
911
412
    def has_workingtree(self):
912
413
        if self._has_working_tree is None:
913
 
            path = self._path_for_remote_call(self._client)
914
 
            try:
915
 
                response = self._call(b'BzrDir.has_workingtree', path)
916
 
            except errors.UnknownSmartMethod:
917
 
                self._ensure_real()
918
 
                self._has_working_tree = self._real_bzrdir.has_workingtree()
919
 
            else:
920
 
                if response[0] not in (b'yes', b'no'):
921
 
                    raise SmartProtocolError(
922
 
                        'unexpected response code %s' % (response,))
923
 
                self._has_working_tree = (response[0] == b'yes')
 
414
            self._ensure_real()
 
415
            self._has_working_tree = self._real_bzrdir.has_workingtree()
924
416
        return self._has_working_tree
925
417
 
926
418
    def open_workingtree(self, recommend_upgrade=True):
931
423
 
932
424
    def _path_for_remote_call(self, client):
933
425
        """Return the path to be used for this bzrdir in a remote call."""
934
 
        remote_path = client.remote_path_from_transport(self.root_transport)
935
 
        if sys.version_info[0] == 3:
936
 
            remote_path = remote_path.decode('utf-8')
937
 
        base_url, segment_parameters = urlutils.split_segment_parameters_raw(
938
 
            remote_path)
939
 
        if sys.version_info[0] == 3:
940
 
            base_url = base_url.encode('utf-8')
941
 
        return base_url
 
426
        return client.remote_path_from_transport(self.root_transport)
942
427
 
943
428
    def get_branch_transport(self, branch_format, name=None):
944
429
        self._ensure_real()
956
441
        """Upgrading of remote bzrdirs is not supported yet."""
957
442
        return False
958
443
 
959
 
    def needs_format_conversion(self, format):
 
444
    def needs_format_conversion(self, format=None):
960
445
        """Upgrading of remote bzrdirs is not supported yet."""
 
446
        if format is None:
 
447
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
 
448
                % 'needs_format_conversion(format=None)')
961
449
        return False
962
450
 
 
451
    def clone(self, url, revision_id=None, force_new_repo=False,
 
452
              preserve_stacking=False):
 
453
        self._ensure_real()
 
454
        return self._real_bzrdir.clone(url, revision_id=revision_id,
 
455
            force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
 
456
 
963
457
    def _get_config(self):
964
458
        return RemoteBzrDirConfig(self)
965
459
 
966
 
    def _get_config_store(self):
967
 
        return RemoteControlStore(self)
968
 
 
969
 
 
970
 
class RemoteInventoryTree(InventoryRevisionTree):
971
 
 
972
 
    def __init__(self, repository, inv, revision_id):
973
 
        super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
974
 
 
975
 
    def archive(self, format, name, root=None, subdir=None, force_mtime=None):
976
 
        ret = self._repository._revision_archive(
977
 
            self.get_revision_id(), format, name, root, subdir,
978
 
            force_mtime=force_mtime)
979
 
        if ret is None:
980
 
            return super(RemoteInventoryTree, self).archive(
981
 
                format, name, root, subdir, force_mtime=force_mtime)
982
 
        return ret
983
 
 
984
 
    def annotate_iter(self, path,
985
 
                      default_revision=_mod_revision.CURRENT_REVISION):
986
 
        """Return an iterator of revision_id, line tuples.
987
 
 
988
 
        For working trees (and mutable trees in general), the special
989
 
        revision_id 'current:' will be used for lines that are new in this
990
 
        tree, e.g. uncommitted changes.
991
 
        :param default_revision: For lines that don't match a basis, mark them
992
 
            with this revision id. Not all implementations will make use of
993
 
            this value.
994
 
        """
995
 
        ret = self._repository._annotate_file_revision(
996
 
            self.get_revision_id(), path, file_id=None,
997
 
            default_revision=default_revision)
998
 
        if ret is None:
999
 
            return super(RemoteInventoryTree, self).annotate_iter(
1000
 
                path, default_revision=default_revision)
1001
 
        return ret
1002
 
 
1003
 
 
1004
 
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
 
460
 
 
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
1005
462
    """Format for repositories accessed over a _SmartClient.
1006
463
 
1007
464
    Instances of this repository are represented by RemoteRepository
1021
478
        to obtain data like the network name.
1022
479
    """
1023
480
 
1024
 
    _matchingcontroldir = RemoteBzrDirFormat()
1025
 
    supports_full_versioned_files = True
1026
 
    supports_leaving_lock = True
1027
 
    supports_overriding_transport = False
 
481
    _matchingbzrdir = RemoteBzrDirFormat()
1028
482
 
1029
483
    def __init__(self):
1030
 
        _mod_repository.RepositoryFormat.__init__(self)
 
484
        repository.RepositoryFormat.__init__(self)
1031
485
        self._custom_format = None
1032
486
        self._network_name = None
1033
487
        self._creating_bzrdir = None
1034
 
        self._revision_graph_can_have_wrong_parents = None
1035
488
        self._supports_chks = None
1036
489
        self._supports_external_lookups = None
1037
490
        self._supports_tree_reference = None
1038
 
        self._supports_funky_characters = None
1039
 
        self._supports_nesting_repositories = None
1040
491
        self._rich_root_data = None
1041
492
 
1042
493
    def __repr__(self):
1043
494
        return "%s(_network_name=%r)" % (self.__class__.__name__,
1044
 
                                         self._network_name)
 
495
            self._network_name)
1045
496
 
1046
497
    @property
1047
498
    def fast_deltas(self):
1071
522
        return self._supports_external_lookups
1072
523
 
1073
524
    @property
1074
 
    def supports_funky_characters(self):
1075
 
        if self._supports_funky_characters is None:
1076
 
            self._ensure_real()
1077
 
            self._supports_funky_characters = \
1078
 
                self._custom_format.supports_funky_characters
1079
 
        return self._supports_funky_characters
1080
 
 
1081
 
    @property
1082
 
    def supports_nesting_repositories(self):
1083
 
        if self._supports_nesting_repositories is None:
1084
 
            self._ensure_real()
1085
 
            self._supports_nesting_repositories = \
1086
 
                self._custom_format.supports_nesting_repositories
1087
 
        return self._supports_nesting_repositories
1088
 
 
1089
 
    @property
1090
525
    def supports_tree_reference(self):
1091
526
        if self._supports_tree_reference is None:
1092
527
            self._ensure_real()
1094
529
                self._custom_format.supports_tree_reference
1095
530
        return self._supports_tree_reference
1096
531
 
1097
 
    @property
1098
 
    def revision_graph_can_have_wrong_parents(self):
1099
 
        if self._revision_graph_can_have_wrong_parents is None:
1100
 
            self._ensure_real()
1101
 
            self._revision_graph_can_have_wrong_parents = \
1102
 
                self._custom_format.revision_graph_can_have_wrong_parents
1103
 
        return self._revision_graph_can_have_wrong_parents
1104
 
 
1105
 
    def _vfs_initialize(self, a_controldir, shared):
 
532
    def _vfs_initialize(self, a_bzrdir, shared):
1106
533
        """Helper for common code in initialize."""
1107
534
        if self._custom_format:
1108
535
            # Custom format requested
1109
 
            result = self._custom_format.initialize(
1110
 
                a_controldir, shared=shared)
 
536
            result = self._custom_format.initialize(a_bzrdir, shared=shared)
1111
537
        elif self._creating_bzrdir is not None:
1112
538
            # Use the format that the repository we were created to back
1113
539
            # has.
1114
540
            prior_repo = self._creating_bzrdir.open_repository()
1115
541
            prior_repo._ensure_real()
1116
542
            result = prior_repo._real_repository._format.initialize(
1117
 
                a_controldir, shared=shared)
 
543
                a_bzrdir, shared=shared)
1118
544
        else:
1119
545
            # assume that a_bzr is a RemoteBzrDir but the smart server didn't
1120
546
            # support remote initialization.
1121
547
            # We delegate to a real object at this point (as RemoteBzrDir
1122
548
            # delegate to the repository format which would lead to infinite
1123
 
            # recursion if we just called a_controldir.create_repository.
1124
 
            a_controldir._ensure_real()
1125
 
            result = a_controldir._real_bzrdir.create_repository(shared=shared)
 
549
            # recursion if we just called a_bzrdir.create_repository.
 
550
            a_bzrdir._ensure_real()
 
551
            result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1126
552
        if not isinstance(result, RemoteRepository):
1127
 
            return self.open(a_controldir)
 
553
            return self.open(a_bzrdir)
1128
554
        else:
1129
555
            return result
1130
556
 
1131
 
    def initialize(self, a_controldir, shared=False):
 
557
    def initialize(self, a_bzrdir, shared=False):
1132
558
        # Being asked to create on a non RemoteBzrDir:
1133
 
        if not isinstance(a_controldir, RemoteBzrDir):
1134
 
            return self._vfs_initialize(a_controldir, shared)
1135
 
        medium = a_controldir._client._medium
 
559
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
560
            return self._vfs_initialize(a_bzrdir, shared)
 
561
        medium = a_bzrdir._client._medium
1136
562
        if medium._is_remote_before((1, 13)):
1137
 
            return self._vfs_initialize(a_controldir, shared)
 
563
            return self._vfs_initialize(a_bzrdir, shared)
1138
564
        # Creating on a remote bzr dir.
1139
565
        # 1) get the network name to use.
1140
566
        if self._custom_format:
1142
568
        elif self._network_name:
1143
569
            network_name = self._network_name
1144
570
        else:
1145
 
            # Select the current breezy default and ask for that.
1146
 
            reference_bzrdir_format = controldir.format_registry.get(
1147
 
                'default')()
 
571
            # Select the current bzrlib default and ask for that.
 
572
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
1148
573
            reference_format = reference_bzrdir_format.repository_format
1149
574
            network_name = reference_format.network_name()
1150
575
        # 2) try direct creation via RPC
1151
 
        path = a_controldir._path_for_remote_call(a_controldir._client)
1152
 
        verb = b'BzrDir.create_repository'
 
576
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
577
        verb = 'BzrDir.create_repository'
1153
578
        if shared:
1154
 
            shared_str = b'True'
 
579
            shared_str = 'True'
1155
580
        else:
1156
 
            shared_str = b'False'
 
581
            shared_str = 'False'
1157
582
        try:
1158
 
            response = a_controldir._call(verb, path, network_name, shared_str)
 
583
            response = a_bzrdir._call(verb, path, network_name, shared_str)
1159
584
        except errors.UnknownSmartMethod:
1160
585
            # Fallback - use vfs methods
1161
586
            medium._remember_remote_is_before((1, 13))
1162
 
            return self._vfs_initialize(a_controldir, shared)
 
587
            return self._vfs_initialize(a_bzrdir, shared)
1163
588
        else:
1164
589
            # Turn the response into a RemoteRepository object.
1165
590
            format = response_tuple_to_repo_format(response[1:])
1166
591
            # Used to support creating a real format instance when needed.
1167
 
            format._creating_bzrdir = a_controldir
1168
 
            remote_repo = RemoteRepository(a_controldir, format)
 
592
            format._creating_bzrdir = a_bzrdir
 
593
            remote_repo = RemoteRepository(a_bzrdir, format)
1169
594
            format._creating_repo = remote_repo
1170
595
            return remote_repo
1171
596
 
1172
 
    def open(self, a_controldir):
1173
 
        if not isinstance(a_controldir, RemoteBzrDir):
1174
 
            raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1175
 
        return a_controldir.open_repository()
 
597
    def open(self, a_bzrdir):
 
598
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
599
            raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
 
600
        return a_bzrdir.open_repository()
1176
601
 
1177
602
    def _ensure_real(self):
1178
603
        if self._custom_format is None:
1179
 
            try:
1180
 
                self._custom_format = _mod_repository.network_format_registry.get(
1181
 
                    self._network_name)
1182
 
            except KeyError:
1183
 
                raise errors.UnknownFormatError(kind='repository',
1184
 
                                                format=self._network_name)
 
604
            self._custom_format = repository.network_format_registry.get(
 
605
                self._network_name)
1185
606
 
1186
607
    @property
1187
608
    def _fetch_order(self):
1222
643
        return self._custom_format._serializer
1223
644
 
1224
645
 
1225
 
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
1226
 
                       lock._RelockDebugMixin):
 
646
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
 
647
    bzrdir.ControlComponent):
1227
648
    """Repository accessed over rpc.
1228
649
 
1229
650
    For the moment most operations are performed using local transport-backed
1245
666
            self._real_repository = real_repository
1246
667
        else:
1247
668
            self._real_repository = None
1248
 
        self.controldir = remote_bzrdir
 
669
        self.bzrdir = remote_bzrdir
1249
670
        if _client is None:
1250
671
            self._client = remote_bzrdir._client
1251
672
        else:
1253
674
        self._format = format
1254
675
        self._lock_mode = None
1255
676
        self._lock_token = None
1256
 
        self._write_group_tokens = None
1257
677
        self._lock_count = 0
1258
678
        self._leave_lock = False
1259
679
        # Cache of revision parents; misses are cached during read locks, and
1269
689
        self._reconcile_does_inventory_gc = False
1270
690
        self._reconcile_fixes_text_parents = False
1271
691
        self._reconcile_backsup_inventory = False
1272
 
        self.base = self.controldir.transport.base
 
692
        self.base = self.bzrdir.transport.base
1273
693
        # Additional places to query for data.
1274
694
        self._fallback_repositories = []
1275
695
 
1276
696
    @property
1277
697
    def user_transport(self):
1278
 
        return self.controldir.user_transport
 
698
        return self.bzrdir.user_transport
1279
699
 
1280
700
    @property
1281
701
    def control_transport(self):
1282
702
        # XXX: Normally you shouldn't directly get at the remote repository
1283
703
        # transport, but I'm not sure it's worth making this method
1284
704
        # optional -- mbp 2010-04-21
1285
 
        return self.controldir.get_repository_transport(None)
1286
 
 
 
705
        return self.bzrdir.get_repository_transport(None)
 
706
        
1287
707
    def __str__(self):
1288
708
        return "%s(%s)" % (self.__class__.__name__, self.base)
1289
709
 
1299
719
 
1300
720
        :param suppress_errors: see Repository.abort_write_group.
1301
721
        """
1302
 
        if self._real_repository:
1303
 
            self._ensure_real()
1304
 
            return self._real_repository.abort_write_group(
1305
 
                suppress_errors=suppress_errors)
1306
 
        if not self.is_in_write_group():
1307
 
            if suppress_errors:
1308
 
                mutter('(suppressed) not in write group')
1309
 
                return
1310
 
            raise errors.BzrError("not in write group")
1311
 
        path = self.controldir._path_for_remote_call(self._client)
1312
 
        try:
1313
 
            response = self._call(b'Repository.abort_write_group', path,
1314
 
                                  self._lock_token,
1315
 
                                  [token.encode('utf-8') for token in self._write_group_tokens])
1316
 
        except Exception as exc:
1317
 
            self._write_group = None
1318
 
            if not suppress_errors:
1319
 
                raise
1320
 
            mutter('abort_write_group failed')
1321
 
            log_exception_quietly()
1322
 
            note(gettext('bzr: ERROR (ignored): %s'), exc)
1323
 
        else:
1324
 
            if response != (b'ok', ):
1325
 
                raise errors.UnexpectedSmartServerResponse(response)
1326
 
            self._write_group_tokens = None
 
722
        self._ensure_real()
 
723
        return self._real_repository.abort_write_group(
 
724
            suppress_errors=suppress_errors)
1327
725
 
1328
726
    @property
1329
727
    def chk_bytes(self):
1343
741
        for older plugins that don't use e.g. the CommitBuilder
1344
742
        facility.
1345
743
        """
1346
 
        if self._real_repository:
1347
 
            self._ensure_real()
1348
 
            return self._real_repository.commit_write_group()
1349
 
        if not self.is_in_write_group():
1350
 
            raise errors.BzrError("not in write group")
1351
 
        path = self.controldir._path_for_remote_call(self._client)
1352
 
        response = self._call(b'Repository.commit_write_group', path,
1353
 
                              self._lock_token, [token.encode('utf-8') for token in self._write_group_tokens])
1354
 
        if response != (b'ok', ):
1355
 
            raise errors.UnexpectedSmartServerResponse(response)
1356
 
        self._write_group_tokens = None
1357
 
        # Refresh data after writing to the repository.
1358
 
        self.refresh_data()
 
744
        self._ensure_real()
 
745
        return self._real_repository.commit_write_group()
1359
746
 
1360
747
    def resume_write_group(self, tokens):
1361
 
        if self._real_repository:
1362
 
            return self._real_repository.resume_write_group(tokens)
1363
 
        path = self.controldir._path_for_remote_call(self._client)
1364
 
        try:
1365
 
            response = self._call(b'Repository.check_write_group', path,
1366
 
                                  self._lock_token, [token.encode('utf-8') for token in tokens])
1367
 
        except errors.UnknownSmartMethod:
1368
 
            self._ensure_real()
1369
 
            return self._real_repository.resume_write_group(tokens)
1370
 
        if response != (b'ok', ):
1371
 
            raise errors.UnexpectedSmartServerResponse(response)
1372
 
        self._write_group_tokens = tokens
 
748
        self._ensure_real()
 
749
        return self._real_repository.resume_write_group(tokens)
1373
750
 
1374
751
    def suspend_write_group(self):
1375
 
        if self._real_repository:
1376
 
            return self._real_repository.suspend_write_group()
1377
 
        ret = self._write_group_tokens or []
1378
 
        self._write_group_tokens = None
1379
 
        return ret
 
752
        self._ensure_real()
 
753
        return self._real_repository.suspend_write_group()
1380
754
 
1381
755
    def get_missing_parent_inventories(self, check_for_missing_texts=True):
1382
756
        self._ensure_real()
1390
764
 
1391
765
    def get_rev_id_for_revno(self, revno, known_pair):
1392
766
        """See Repository.get_rev_id_for_revno."""
1393
 
        path = self.controldir._path_for_remote_call(self._client)
 
767
        path = self.bzrdir._path_for_remote_call(self._client)
1394
768
        try:
1395
769
            if self._client._medium._is_remote_before((1, 17)):
1396
770
                return self._get_rev_id_for_revno_vfs(revno, known_pair)
1397
771
            response = self._call(
1398
 
                b'Repository.get_rev_id_for_revno', path, revno, known_pair)
 
772
                'Repository.get_rev_id_for_revno', path, revno, known_pair)
1399
773
        except errors.UnknownSmartMethod:
1400
774
            self._client._medium._remember_remote_is_before((1, 17))
1401
775
            return self._get_rev_id_for_revno_vfs(revno, known_pair)
1402
 
        except errors.UnknownErrorFromSmartServer as e:
1403
 
            # Older versions of Bazaar/Breezy (<< 3.0.0) would raise a
1404
 
            # ValueError instead of returning revno-outofbounds
1405
 
            if len(e.error_tuple) < 3:
1406
 
                raise
1407
 
            if e.error_tuple[:2] != (b'error', b'ValueError'):
1408
 
                raise
1409
 
            m = re.match(
1410
 
                br"requested revno \(([0-9]+)\) is later than given "
1411
 
                br"known revno \(([0-9]+)\)", e.error_tuple[2])
1412
 
            if not m:
1413
 
                raise
1414
 
            raise errors.RevnoOutOfBounds(
1415
 
                int(m.group(1)), (0, int(m.group(2))))
1416
 
        if response[0] == b'ok':
 
776
        if response[0] == 'ok':
1417
777
            return True, response[1]
1418
 
        elif response[0] == b'history-incomplete':
 
778
        elif response[0] == 'history-incomplete':
1419
779
            known_pair = response[1:3]
1420
780
            for fallback in self._fallback_repositories:
1421
 
                found, result = fallback.get_rev_id_for_revno(
1422
 
                    revno, known_pair)
 
781
                found, result = fallback.get_rev_id_for_revno(revno, known_pair)
1423
782
                if found:
1424
783
                    return True, result
1425
784
                else:
1446
805
            if 'hpssvfs' in debug.debug_flags:
1447
806
                import traceback
1448
807
                warning('VFS Repository access triggered\n%s',
1449
 
                        ''.join(traceback.format_stack()))
 
808
                    ''.join(traceback.format_stack()))
1450
809
            self._unstacked_provider.missing_keys.clear()
1451
 
            self.controldir._ensure_real()
 
810
            self.bzrdir._ensure_real()
1452
811
            self._set_real_repository(
1453
 
                self.controldir._real_bzrdir.open_repository())
 
812
                self.bzrdir._real_bzrdir.open_repository())
1454
813
 
1455
814
    def _translate_error(self, err, **context):
1456
 
        self.controldir._translate_error(err, repository=self, **context)
 
815
        self.bzrdir._translate_error(err, repository=self, **context)
1457
816
 
1458
817
    def find_text_key_references(self):
1459
818
        """Find the text key references within the repository.
1460
819
 
 
820
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
 
821
        revision_ids. Each altered file-ids has the exact revision_ids that
 
822
        altered it listed explicitly.
1461
823
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
1462
824
            to whether they were referred to by the inventory of the
1463
825
            revision_id that they contain. The inventory texts from all present
1480
842
    def _get_revision_graph(self, revision_id):
1481
843
        """Private method for using with old (< 1.2) servers to fallback."""
1482
844
        if revision_id is None:
1483
 
            revision_id = b''
1484
 
        elif _mod_revision.is_null(revision_id):
 
845
            revision_id = ''
 
846
        elif revision.is_null(revision_id):
1485
847
            return {}
1486
848
 
1487
 
        path = self.controldir._path_for_remote_call(self._client)
 
849
        path = self.bzrdir._path_for_remote_call(self._client)
1488
850
        response = self._call_expecting_body(
1489
 
            b'Repository.get_revision_graph', path, revision_id)
 
851
            'Repository.get_revision_graph', path, revision_id)
1490
852
        response_tuple, response_handler = response
1491
 
        if response_tuple[0] != b'ok':
 
853
        if response_tuple[0] != 'ok':
1492
854
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1493
855
        coded = response_handler.read_body_bytes()
1494
 
        if coded == b'':
 
856
        if coded == '':
1495
857
            # no revisions in this repository!
1496
858
            return {}
1497
 
        lines = coded.split(b'\n')
 
859
        lines = coded.split('\n')
1498
860
        revision_graph = {}
1499
861
        for line in lines:
1500
862
            d = tuple(line.split())
1510
872
        """Return a source for streaming from this repository."""
1511
873
        return RemoteStreamSource(self, to_format)
1512
874
 
1513
 
    def get_file_graph(self):
1514
 
        with self.lock_read():
1515
 
            return graph.Graph(self.texts)
1516
 
 
 
875
    @needs_read_lock
1517
876
    def has_revision(self, revision_id):
1518
877
        """True if this repository has a copy of the revision."""
1519
 
        # Copy of breezy.repository.Repository.has_revision
1520
 
        with self.lock_read():
1521
 
            return revision_id in self.has_revisions((revision_id,))
 
878
        # Copy of bzrlib.repository.Repository.has_revision
 
879
        return revision_id in self.has_revisions((revision_id,))
1522
880
 
 
881
    @needs_read_lock
1523
882
    def has_revisions(self, revision_ids):
1524
883
        """Probe to find out the presence of multiple revisions.
1525
884
 
1526
885
        :param revision_ids: An iterable of revision_ids.
1527
886
        :return: A set of the revision_ids that were present.
1528
887
        """
1529
 
        with self.lock_read():
1530
 
            # Copy of breezy.repository.Repository.has_revisions
1531
 
            parent_map = self.get_parent_map(revision_ids)
1532
 
            result = set(parent_map)
1533
 
            if _mod_revision.NULL_REVISION in revision_ids:
1534
 
                result.add(_mod_revision.NULL_REVISION)
1535
 
            return result
 
888
        # Copy of bzrlib.repository.Repository.has_revisions
 
889
        parent_map = self.get_parent_map(revision_ids)
 
890
        result = set(parent_map)
 
891
        if _mod_revision.NULL_REVISION in revision_ids:
 
892
            result.add(_mod_revision.NULL_REVISION)
 
893
        return result
1536
894
 
1537
895
    def _has_same_fallbacks(self, other_repo):
1538
896
        """Returns true if the repositories have the same fallbacks."""
1539
897
        # XXX: copied from Repository; it should be unified into a base class
1540
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
898
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
1541
899
        my_fb = self._fallback_repositories
1542
900
        other_fb = other_repo._fallback_repositories
1543
901
        if len(my_fb) != len(other_fb):
1551
909
        # TODO: Move to RepositoryBase and unify with the regular Repository
1552
910
        # one; unfortunately the tests rely on slightly different behaviour at
1553
911
        # present -- mbp 20090710
1554
 
        return (self.__class__ is other.__class__
1555
 
                and self.controldir.transport.base == other.controldir.transport.base)
 
912
        return (self.__class__ is other.__class__ and
 
913
                self.bzrdir.transport.base == other.bzrdir.transport.base)
1556
914
 
1557
915
    def get_graph(self, other_repository=None):
1558
916
        """Return the graph for this repository format"""
1559
917
        parents_provider = self._make_parents_provider(other_repository)
1560
918
        return graph.Graph(parents_provider)
1561
919
 
 
920
    @needs_read_lock
1562
921
    def get_known_graph_ancestry(self, revision_ids):
1563
922
        """Return the known graph for a set of revision ids and their ancestors.
1564
923
        """
1565
 
        with self.lock_read():
1566
 
            revision_graph = dict(((key, value) for key, value in
1567
 
                                   self.get_graph().iter_ancestry(revision_ids) if value is not None))
1568
 
            revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1569
 
            return graph.KnownGraph(revision_graph)
 
924
        st = static_tuple.StaticTuple
 
925
        revision_keys = [st(r_id).intern() for r_id in revision_ids]
 
926
        known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
 
927
        return graph.GraphThunkIdsToKeys(known_graph)
1570
928
 
1571
929
    def gather_stats(self, revid=None, committers=None):
1572
930
        """See Repository.gather_stats()."""
1573
 
        path = self.controldir._path_for_remote_call(self._client)
 
931
        path = self.bzrdir._path_for_remote_call(self._client)
1574
932
        # revid can be None to indicate no revisions, not just NULL_REVISION
1575
 
        if revid is None or _mod_revision.is_null(revid):
1576
 
            fmt_revid = b''
 
933
        if revid is None or revision.is_null(revid):
 
934
            fmt_revid = ''
1577
935
        else:
1578
936
            fmt_revid = revid
1579
937
        if committers is None or not committers:
1580
 
            fmt_committers = b'no'
 
938
            fmt_committers = 'no'
1581
939
        else:
1582
 
            fmt_committers = b'yes'
 
940
            fmt_committers = 'yes'
1583
941
        response_tuple, response_handler = self._call_expecting_body(
1584
 
            b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1585
 
        if response_tuple[0] != b'ok':
 
942
            'Repository.gather_stats', path, fmt_revid, fmt_committers)
 
943
        if response_tuple[0] != 'ok':
1586
944
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1587
945
 
1588
946
        body = response_handler.read_body_bytes()
1589
947
        result = {}
1590
 
        for line in body.split(b'\n'):
 
948
        for line in body.split('\n'):
1591
949
            if not line:
1592
950
                continue
1593
 
            key, val_text = line.split(b':')
1594
 
            key = key.decode('ascii')
 
951
            key, val_text = line.split(':')
1595
952
            if key in ('revisions', 'size', 'committers'):
1596
953
                result[key] = int(val_text)
1597
954
            elif key in ('firstrev', 'latestrev'):
1598
 
                values = val_text.split(b' ')[1:]
1599
 
                result[key] = (float(values[0]), int(values[1]))
 
955
                values = val_text.split(' ')[1:]
 
956
                result[key] = (float(values[0]), long(values[1]))
1600
957
 
1601
958
        return result
1602
959
 
1608
965
 
1609
966
    def get_physical_lock_status(self):
1610
967
        """See Repository.get_physical_lock_status()."""
1611
 
        path = self.controldir._path_for_remote_call(self._client)
1612
 
        try:
1613
 
            response = self._call(b'Repository.get_physical_lock_status', path)
1614
 
        except errors.UnknownSmartMethod:
1615
 
            self._ensure_real()
1616
 
            return self._real_repository.get_physical_lock_status()
1617
 
        if response[0] not in (b'yes', b'no'):
1618
 
            raise errors.UnexpectedSmartServerResponse(response)
1619
 
        return (response[0] == b'yes')
 
968
        # should be an API call to the server.
 
969
        self._ensure_real()
 
970
        return self._real_repository.get_physical_lock_status()
1620
971
 
1621
972
    def is_in_write_group(self):
1622
973
        """Return True if there is an open write group.
1623
974
 
1624
975
        write groups are only applicable locally for the smart server..
1625
976
        """
1626
 
        if self._write_group_tokens is not None:
1627
 
            return True
1628
977
        if self._real_repository:
1629
978
            return self._real_repository.is_in_write_group()
1630
979
 
1633
982
 
1634
983
    def is_shared(self):
1635
984
        """See Repository.is_shared()."""
1636
 
        path = self.controldir._path_for_remote_call(self._client)
1637
 
        response = self._call(b'Repository.is_shared', path)
1638
 
        if response[0] not in (b'yes', b'no'):
1639
 
            raise SmartProtocolError(
1640
 
                'unexpected response code %s' % (response,))
1641
 
        return response[0] == b'yes'
 
985
        path = self.bzrdir._path_for_remote_call(self._client)
 
986
        response = self._call('Repository.is_shared', path)
 
987
        if response[0] not in ('yes', 'no'):
 
988
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
989
        return response[0] == 'yes'
1642
990
 
1643
991
    def is_write_locked(self):
1644
992
        return self._lock_mode == 'w'
1649
997
        pass
1650
998
 
1651
999
    def lock_read(self):
1652
 
        """Lock the repository for read operations.
1653
 
 
1654
 
        :return: A breezy.lock.LogicalLockResult.
1655
 
        """
1656
1000
        # wrong eventually - want a local lock cache context
1657
1001
        if not self._lock_mode:
1658
1002
            self._note_lock('r')
1665
1009
                repo.lock_read()
1666
1010
        else:
1667
1011
            self._lock_count += 1
1668
 
        return lock.LogicalLockResult(self.unlock)
1669
1012
 
1670
1013
    def _remote_lock_write(self, token):
1671
 
        path = self.controldir._path_for_remote_call(self._client)
 
1014
        path = self.bzrdir._path_for_remote_call(self._client)
1672
1015
        if token is None:
1673
 
            token = b''
 
1016
            token = ''
1674
1017
        err_context = {'token': token}
1675
 
        response = self._call(b'Repository.lock_write', path, token,
 
1018
        response = self._call('Repository.lock_write', path, token,
1676
1019
                              **err_context)
1677
 
        if response[0] == b'ok':
 
1020
        if response[0] == 'ok':
1678
1021
            ok, token = response
1679
1022
            return token
1680
1023
        else:
1711
1054
            raise errors.ReadOnlyError(self)
1712
1055
        else:
1713
1056
            self._lock_count += 1
1714
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
1057
        return self._lock_token or None
1715
1058
 
1716
1059
    def leave_lock_in_place(self):
1717
1060
        if not self._lock_token:
1752
1095
        # 3) new servers, RemoteRepository.ensure_real is triggered before
1753
1096
        # RemoteBranch.ensure real, in this case we get a repo with no fallbacks
1754
1097
        # and need to populate it.
1755
 
        if (self._fallback_repositories
1756
 
            and len(self._real_repository._fallback_repositories)
1757
 
                != len(self._fallback_repositories)):
 
1098
        if (self._fallback_repositories and
 
1099
            len(self._real_repository._fallback_repositories) !=
 
1100
            len(self._fallback_repositories)):
1758
1101
            if len(self._real_repository._fallback_repositories):
1759
1102
                raise AssertionError(
1760
1103
                    "cannot cleanly remove existing _fallback_repositories")
1766
1109
            self._real_repository.lock_write(self._lock_token)
1767
1110
        elif self._lock_mode == 'r':
1768
1111
            self._real_repository.lock_read()
1769
 
        if self._write_group_tokens is not None:
1770
 
            # if we are already in a write group, resume it
1771
 
            self._real_repository.resume_write_group(self._write_group_tokens)
1772
 
            self._write_group_tokens = None
1773
1112
 
1774
1113
    def start_write_group(self):
1775
1114
        """Start a write group on the decorated repository.
1779
1118
        for older plugins that don't use e.g. the CommitBuilder
1780
1119
        facility.
1781
1120
        """
1782
 
        if self._real_repository:
1783
 
            self._ensure_real()
1784
 
            return self._real_repository.start_write_group()
1785
 
        if not self.is_write_locked():
1786
 
            raise errors.NotWriteLocked(self)
1787
 
        if self._write_group_tokens is not None:
1788
 
            raise errors.BzrError('already in a write group')
1789
 
        path = self.controldir._path_for_remote_call(self._client)
1790
 
        try:
1791
 
            response = self._call(b'Repository.start_write_group', path,
1792
 
                                  self._lock_token)
1793
 
        except (errors.UnknownSmartMethod, errors.UnsuspendableWriteGroup):
1794
 
            self._ensure_real()
1795
 
            return self._real_repository.start_write_group()
1796
 
        if response[0] != b'ok':
1797
 
            raise errors.UnexpectedSmartServerResponse(response)
1798
 
        self._write_group_tokens = [
1799
 
            token.decode('utf-8') for token in response[1]]
 
1121
        self._ensure_real()
 
1122
        return self._real_repository.start_write_group()
1800
1123
 
1801
1124
    def _unlock(self, token):
1802
 
        path = self.controldir._path_for_remote_call(self._client)
 
1125
        path = self.bzrdir._path_for_remote_call(self._client)
1803
1126
        if not token:
1804
1127
            # with no token the remote repository is not persistently locked.
1805
1128
            return
1806
1129
        err_context = {'token': token}
1807
 
        response = self._call(b'Repository.unlock', path, token,
 
1130
        response = self._call('Repository.unlock', path, token,
1808
1131
                              **err_context)
1809
 
        if response == (b'ok',):
 
1132
        if response == ('ok',):
1810
1133
            return
1811
1134
        else:
1812
1135
            raise errors.UnexpectedSmartServerResponse(response)
1829
1152
            # This is just to let the _real_repository stay up to date.
1830
1153
            if self._real_repository is not None:
1831
1154
                self._real_repository.unlock()
1832
 
            elif self._write_group_tokens is not None:
1833
 
                self.abort_write_group()
1834
1155
        finally:
1835
1156
            # The rpc-level lock should be released even if there was a
1836
1157
            # problem releasing the vfs-based lock.
1848
1169
 
1849
1170
    def break_lock(self):
1850
1171
        # should hand off to the network
1851
 
        path = self.controldir._path_for_remote_call(self._client)
1852
 
        try:
1853
 
            response = self._call(b"Repository.break_lock", path)
1854
 
        except errors.UnknownSmartMethod:
1855
 
            self._ensure_real()
1856
 
            return self._real_repository.break_lock()
1857
 
        if response != (b'ok',):
1858
 
            raise errors.UnexpectedSmartServerResponse(response)
 
1172
        self._ensure_real()
 
1173
        return self._real_repository.break_lock()
1859
1174
 
1860
1175
    def _get_tarball(self, compression):
1861
1176
        """Return a TemporaryFile containing a repository tarball.
1863
1178
        Returns None if the server does not support sending tarballs.
1864
1179
        """
1865
1180
        import tempfile
1866
 
        path = self.controldir._path_for_remote_call(self._client)
 
1181
        path = self.bzrdir._path_for_remote_call(self._client)
1867
1182
        try:
1868
1183
            response, protocol = self._call_expecting_body(
1869
 
                b'Repository.tarball', path, compression.encode('ascii'))
 
1184
                'Repository.tarball', path, compression)
1870
1185
        except errors.UnknownSmartMethod:
1871
1186
            protocol.cancel_read_body()
1872
1187
            return None
1873
 
        if response[0] == b'ok':
 
1188
        if response[0] == 'ok':
1874
1189
            # Extract the tarball and return it
1875
1190
            t = tempfile.NamedTemporaryFile()
1876
1191
            # TODO: rpc layer should read directly into it...
1880
1195
        raise errors.UnexpectedSmartServerResponse(response)
1881
1196
 
1882
1197
    def sprout(self, to_bzrdir, revision_id=None):
1883
 
        """Create a descendent repository for new development.
1884
 
 
1885
 
        Unlike clone, this does not copy the settings of the repository.
1886
 
        """
1887
 
        with self.lock_read():
1888
 
            dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1889
 
            dest_repo.fetch(self, revision_id=revision_id)
1890
 
            return dest_repo
1891
 
 
1892
 
    def _create_sprouting_repo(self, a_controldir, shared):
1893
 
        if not isinstance(a_controldir._format, self.controldir._format.__class__):
1894
 
            # use target default format.
1895
 
            dest_repo = a_controldir.create_repository()
1896
 
        else:
1897
 
            # Most control formats need the repository to be specifically
1898
 
            # created, but on some old all-in-one formats it's not needed
1899
 
            try:
1900
 
                dest_repo = self._format.initialize(
1901
 
                    a_controldir, shared=shared)
1902
 
            except errors.UninitializableFormat:
1903
 
                dest_repo = a_controldir.open_repository()
 
1198
        # TODO: Option to control what format is created?
 
1199
        self._ensure_real()
 
1200
        dest_repo = self._real_repository._format.initialize(to_bzrdir,
 
1201
                                                             shared=False)
 
1202
        dest_repo.fetch(self, revision_id=revision_id)
1904
1203
        return dest_repo
1905
1204
 
1906
 
    # These methods are just thin shims to the VFS object for now.
 
1205
    ### These methods are just thin shims to the VFS object for now.
1907
1206
 
1908
1207
    def revision_tree(self, revision_id):
1909
 
        with self.lock_read():
1910
 
            revision_id = _mod_revision.ensure_null(revision_id)
1911
 
            if revision_id == _mod_revision.NULL_REVISION:
1912
 
                return InventoryRevisionTree(self,
1913
 
                                             Inventory(root_id=None), _mod_revision.NULL_REVISION)
1914
 
            else:
1915
 
                return list(self.revision_trees([revision_id]))[0]
 
1208
        self._ensure_real()
 
1209
        return self._real_repository.revision_tree(revision_id)
1916
1210
 
1917
1211
    def get_serializer_format(self):
1918
 
        path = self.controldir._path_for_remote_call(self._client)
1919
 
        try:
1920
 
            response = self._call(b'VersionedFileRepository.get_serializer_format',
1921
 
                                  path)
1922
 
        except errors.UnknownSmartMethod:
1923
 
            self._ensure_real()
1924
 
            return self._real_repository.get_serializer_format()
1925
 
        if response[0] != b'ok':
1926
 
            raise errors.UnexpectedSmartServerResponse(response)
1927
 
        return response[1]
 
1212
        self._ensure_real()
 
1213
        return self._real_repository.get_serializer_format()
1928
1214
 
1929
1215
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1930
1216
                           timezone=None, committer=None, revprops=None,
1931
 
                           revision_id=None, lossy=False):
1932
 
        """Obtain a CommitBuilder for this repository.
1933
 
 
1934
 
        :param branch: Branch to commit to.
1935
 
        :param parents: Revision ids of the parents of the new revision.
1936
 
        :param config: Configuration to use.
1937
 
        :param timestamp: Optional timestamp recorded for commit.
1938
 
        :param timezone: Optional timezone for timestamp.
1939
 
        :param committer: Optional committer to set for commit.
1940
 
        :param revprops: Optional dictionary of revision properties.
1941
 
        :param revision_id: Optional revision id.
1942
 
        :param lossy: Whether to discard data that can not be natively
1943
 
            represented, when pushing to a foreign VCS
1944
 
        """
1945
 
        if self._fallback_repositories and not self._format.supports_chks:
1946
 
            raise errors.BzrError("Cannot commit directly to a stacked branch"
1947
 
                                  " in pre-2a formats. See "
1948
 
                                  "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1949
 
        commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1950
 
        result = commit_builder_kls(self, parents, config,
1951
 
                                    timestamp, timezone, committer, revprops, revision_id,
1952
 
                                    lossy)
1953
 
        self.start_write_group()
1954
 
        return result
 
1217
                           revision_id=None):
 
1218
        # FIXME: It ought to be possible to call this without immediately
 
1219
        # triggering _ensure_real.  For now it's the easiest thing to do.
 
1220
        self._ensure_real()
 
1221
        real_repo = self._real_repository
 
1222
        builder = real_repo.get_commit_builder(branch, parents,
 
1223
                config, timestamp=timestamp, timezone=timezone,
 
1224
                committer=committer, revprops=revprops, revision_id=revision_id)
 
1225
        return builder
1955
1226
 
1956
1227
    def add_fallback_repository(self, repository):
1957
1228
        """Add a repository to use for looking up data not held locally.
1964
1235
        # We need to accumulate additional repositories here, to pass them in
1965
1236
        # on various RPC's.
1966
1237
        #
1967
 
        # Make the check before we lock: this raises an exception.
1968
 
        self._check_fallback_repository(repository)
1969
1238
        if self.is_locked():
1970
1239
            # We will call fallback.unlock() when we transition to the unlocked
1971
1240
            # state, so always add a lock here. If a caller passes us a locked
1972
1241
            # repository, they are responsible for unlocking it later.
1973
1242
            repository.lock_read()
 
1243
        self._check_fallback_repository(repository)
1974
1244
        self._fallback_repositories.append(repository)
1975
1245
        # If self._real_repository was parameterised already (e.g. because a
1976
1246
        # _real_branch had its get_stacked_on_url method called), then the
1977
1247
        # repository to be added may already be in the _real_repositories list.
1978
1248
        if self._real_repository is not None:
1979
1249
            fallback_locations = [repo.user_url for repo in
1980
 
                                  self._real_repository._fallback_repositories]
 
1250
                self._real_repository._fallback_repositories]
1981
1251
            if repository.user_url not in fallback_locations:
1982
1252
                self._real_repository.add_fallback_repository(repository)
1983
1253
 
1996
1266
        return self._real_repository.add_inventory(revid, inv, parents)
1997
1267
 
1998
1268
    def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1999
 
                               parents, basis_inv=None, propagate_caches=False):
 
1269
            parents, basis_inv=None, propagate_caches=False):
2000
1270
        self._ensure_real()
2001
1271
        return self._real_repository.add_inventory_by_delta(basis_revision_id,
2002
 
                                                            delta, new_revision_id, parents, basis_inv=basis_inv,
2003
 
                                                            propagate_caches=propagate_caches)
2004
 
 
2005
 
    def add_revision(self, revision_id, rev, inv=None):
2006
 
        _mod_revision.check_not_reserved_id(revision_id)
2007
 
        key = (revision_id,)
2008
 
        # check inventory present
2009
 
        if not self.inventories.get_parent_map([key]):
2010
 
            if inv is None:
2011
 
                raise errors.WeaveRevisionNotPresent(revision_id,
2012
 
                                                     self.inventories)
2013
 
            else:
2014
 
                # yes, this is not suitable for adding with ghosts.
2015
 
                rev.inventory_sha1 = self.add_inventory(revision_id, inv,
2016
 
                                                        rev.parent_ids)
2017
 
        else:
2018
 
            rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
2019
 
        self._add_revision(rev)
2020
 
 
2021
 
    def _add_revision(self, rev):
2022
 
        if self._real_repository is not None:
2023
 
            return self._real_repository._add_revision(rev)
2024
 
        lines = self._serializer.write_revision_to_lines(rev)
2025
 
        key = (rev.revision_id,)
2026
 
        parents = tuple((parent,) for parent in rev.parent_ids)
2027
 
        self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
2028
 
            [('revisions', [ChunkedContentFactory(key, parents, None, lines, chunks_are_lines=True)])],
2029
 
            self._format, self._write_group_tokens)
2030
 
 
 
1272
            delta, new_revision_id, parents, basis_inv=basis_inv,
 
1273
            propagate_caches=propagate_caches)
 
1274
 
 
1275
    def add_revision(self, rev_id, rev, inv=None, config=None):
 
1276
        self._ensure_real()
 
1277
        return self._real_repository.add_revision(
 
1278
            rev_id, rev, inv=inv, config=config)
 
1279
 
 
1280
    @needs_read_lock
2031
1281
    def get_inventory(self, revision_id):
2032
 
        with self.lock_read():
2033
 
            return list(self.iter_inventories([revision_id]))[0]
2034
 
 
2035
 
    def _iter_inventories_rpc(self, revision_ids, ordering):
2036
 
        if ordering is None:
2037
 
            ordering = 'unordered'
2038
 
        path = self.controldir._path_for_remote_call(self._client)
2039
 
        body = b"\n".join(revision_ids)
2040
 
        response_tuple, response_handler = (
2041
 
            self._call_with_body_bytes_expecting_body(
2042
 
                b"VersionedFileRepository.get_inventories",
2043
 
                (path, ordering.encode('ascii')), body))
2044
 
        if response_tuple[0] != b"ok":
2045
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2046
 
        deserializer = inventory_delta.InventoryDeltaDeserializer()
2047
 
        byte_stream = response_handler.read_streamed_body()
2048
 
        decoded = smart_repo._byte_stream_to_stream(byte_stream)
2049
 
        if decoded is None:
2050
 
            # no results whatsoever
2051
 
            return
2052
 
        src_format, stream = decoded
2053
 
        if src_format.network_name() != self._format.network_name():
2054
 
            raise AssertionError(
2055
 
                "Mismatched RemoteRepository and stream src %r, %r" % (
2056
 
                    src_format.network_name(), self._format.network_name()))
2057
 
        # ignore the src format, it's not really relevant
2058
 
        prev_inv = Inventory(root_id=None,
2059
 
                             revision_id=_mod_revision.NULL_REVISION)
2060
 
        # there should be just one substream, with inventory deltas
2061
 
        try:
2062
 
            substream_kind, substream = next(stream)
2063
 
        except StopIteration:
2064
 
            return
2065
 
        if substream_kind != "inventory-deltas":
2066
 
            raise AssertionError(
2067
 
                "Unexpected stream %r received" % substream_kind)
2068
 
        for record in substream:
2069
 
            (parent_id, new_id, versioned_root, tree_references, invdelta) = (
2070
 
                deserializer.parse_text_bytes(record.get_bytes_as("lines")))
2071
 
            if parent_id != prev_inv.revision_id:
2072
 
                raise AssertionError("invalid base %r != %r" % (parent_id,
2073
 
                                                                prev_inv.revision_id))
2074
 
            inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2075
 
            yield inv, inv.revision_id
2076
 
            prev_inv = inv
2077
 
 
2078
 
    def _iter_inventories_vfs(self, revision_ids, ordering=None):
2079
1282
        self._ensure_real()
2080
 
        return self._real_repository._iter_inventories(revision_ids, ordering)
 
1283
        return self._real_repository.get_inventory(revision_id)
2081
1284
 
2082
1285
    def iter_inventories(self, revision_ids, ordering=None):
2083
 
        """Get many inventories by revision_ids.
2084
 
 
2085
 
        This will buffer some or all of the texts used in constructing the
2086
 
        inventories in memory, but will only parse a single inventory at a
2087
 
        time.
2088
 
 
2089
 
        :param revision_ids: The expected revision ids of the inventories.
2090
 
        :param ordering: optional ordering, e.g. 'topological'.  If not
2091
 
            specified, the order of revision_ids will be preserved (by
2092
 
            buffering if necessary).
2093
 
        :return: An iterator of inventories.
2094
 
        """
2095
 
        if ((None in revision_ids) or
2096
 
                (_mod_revision.NULL_REVISION in revision_ids)):
2097
 
            raise ValueError('cannot get null revision inventory')
2098
 
        for inv, revid in self._iter_inventories(revision_ids, ordering):
2099
 
            if inv is None:
2100
 
                raise errors.NoSuchRevision(self, revid)
2101
 
            yield inv
2102
 
 
2103
 
    def _iter_inventories(self, revision_ids, ordering=None):
2104
 
        if len(revision_ids) == 0:
2105
 
            return
2106
 
        missing = set(revision_ids)
2107
 
        if ordering is None:
2108
 
            order_as_requested = True
2109
 
            invs = {}
2110
 
            order = list(revision_ids)
2111
 
            order.reverse()
2112
 
            next_revid = order.pop()
2113
 
        else:
2114
 
            order_as_requested = False
2115
 
            if ordering != 'unordered' and self._fallback_repositories:
2116
 
                raise ValueError('unsupported ordering %r' % ordering)
2117
 
        iter_inv_fns = [self._iter_inventories_rpc] + [
2118
 
            fallback._iter_inventories for fallback in
2119
 
            self._fallback_repositories]
2120
 
        try:
2121
 
            for iter_inv in iter_inv_fns:
2122
 
                request = [revid for revid in revision_ids if revid in missing]
2123
 
                for inv, revid in iter_inv(request, ordering):
2124
 
                    if inv is None:
2125
 
                        continue
2126
 
                    missing.remove(inv.revision_id)
2127
 
                    if ordering != 'unordered':
2128
 
                        invs[revid] = inv
2129
 
                    else:
2130
 
                        yield inv, revid
2131
 
                if order_as_requested:
2132
 
                    # Yield as many results as we can while preserving order.
2133
 
                    while next_revid in invs:
2134
 
                        inv = invs.pop(next_revid)
2135
 
                        yield inv, inv.revision_id
2136
 
                        try:
2137
 
                            next_revid = order.pop()
2138
 
                        except IndexError:
2139
 
                            # We still want to fully consume the stream, just
2140
 
                            # in case it is not actually finished at this point
2141
 
                            next_revid = None
2142
 
                            break
2143
 
        except errors.UnknownSmartMethod:
2144
 
            for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2145
 
                yield inv, revid
2146
 
            return
2147
 
        # Report missing
2148
 
        if order_as_requested:
2149
 
            if next_revid is not None:
2150
 
                yield None, next_revid
2151
 
            while order:
2152
 
                revid = order.pop()
2153
 
                yield invs.get(revid), revid
2154
 
        else:
2155
 
            while missing:
2156
 
                yield None, missing.pop()
2157
 
 
 
1286
        self._ensure_real()
 
1287
        return self._real_repository.iter_inventories(revision_ids, ordering)
 
1288
 
 
1289
    @needs_read_lock
2158
1290
    def get_revision(self, revision_id):
2159
 
        with self.lock_read():
2160
 
            return self.get_revisions([revision_id])[0]
 
1291
        self._ensure_real()
 
1292
        return self._real_repository.get_revision(revision_id)
2161
1293
 
2162
1294
    def get_transaction(self):
2163
1295
        self._ensure_real()
2164
1296
        return self._real_repository.get_transaction()
2165
1297
 
2166
 
    def clone(self, a_controldir, revision_id=None):
2167
 
        with self.lock_read():
2168
 
            dest_repo = self._create_sprouting_repo(
2169
 
                a_controldir, shared=self.is_shared())
2170
 
            self.copy_content_into(dest_repo, revision_id)
2171
 
            return dest_repo
 
1298
    @needs_read_lock
 
1299
    def clone(self, a_bzrdir, revision_id=None):
 
1300
        self._ensure_real()
 
1301
        return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2172
1302
 
2173
1303
    def make_working_trees(self):
2174
1304
        """See Repository.make_working_trees"""
2175
 
        path = self.controldir._path_for_remote_call(self._client)
2176
 
        try:
2177
 
            response = self._call(b'Repository.make_working_trees', path)
2178
 
        except errors.UnknownSmartMethod:
2179
 
            self._ensure_real()
2180
 
            return self._real_repository.make_working_trees()
2181
 
        if response[0] not in (b'yes', b'no'):
2182
 
            raise SmartProtocolError(
2183
 
                'unexpected response code %s' % (response,))
2184
 
        return response[0] == b'yes'
 
1305
        self._ensure_real()
 
1306
        return self._real_repository.make_working_trees()
2185
1307
 
2186
1308
    def refresh_data(self):
2187
 
        """Re-read any data needed to synchronise with disk.
 
1309
        """Re-read any data needed to to synchronise with disk.
2188
1310
 
2189
1311
        This method is intended to be called after another repository instance
2190
1312
        (such as one used by a smart server) has inserted data into the
2191
 
        repository. On all repositories this will work outside of write groups.
2192
 
        Some repository formats (pack and newer for breezy native formats)
2193
 
        support refresh_data inside write groups. If called inside a write
2194
 
        group on a repository that does not support refreshing in a write group
2195
 
        IsInWriteGroupError will be raised.
 
1313
        repository. It may not be called during a write group, but may be
 
1314
        called at any other time.
2196
1315
        """
 
1316
        if self.is_in_write_group():
 
1317
            raise errors.InternalBzrError(
 
1318
                "May not refresh_data while in a write group.")
2197
1319
        if self._real_repository is not None:
2198
1320
            self._real_repository.refresh_data()
2199
 
        # Refresh the parents cache for this object
2200
 
        self._unstacked_provider.disable_cache()
2201
 
        self._unstacked_provider.enable_cache()
2202
1321
 
2203
1322
    def revision_ids_to_search_result(self, result_set):
2204
1323
        """Convert a set of revision ids to a graph SearchResult."""
2205
1324
        result_parents = set()
2206
 
        for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
 
1325
        for parents in self.get_graph().get_parent_map(
 
1326
            result_set).itervalues():
2207
1327
            result_parents.update(parents)
2208
1328
        included_keys = result_set.intersection(result_parents)
2209
1329
        start_keys = result_set.difference(included_keys)
2210
1330
        exclude_keys = result_parents.difference(result_set)
2211
 
        result = vf_search.SearchResult(start_keys, exclude_keys,
2212
 
                                        len(result_set), result_set)
 
1331
        result = graph.SearchResult(start_keys, exclude_keys,
 
1332
            len(result_set), result_set)
2213
1333
        return result
2214
1334
 
2215
 
    def search_missing_revision_ids(self, other,
2216
 
                                    find_ghosts=True, revision_ids=None, if_present_ids=None,
2217
 
                                    limit=None):
 
1335
    @needs_read_lock
 
1336
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2218
1337
        """Return the revision ids that other has that this does not.
2219
1338
 
2220
1339
        These are returned in topological order.
2221
1340
 
2222
1341
        revision_id: only return revision ids included by revision_id.
2223
1342
        """
2224
 
        with self.lock_read():
2225
 
            inter_repo = _mod_repository.InterRepository.get(other, self)
2226
 
            return inter_repo.search_missing_revision_ids(
2227
 
                find_ghosts=find_ghosts, revision_ids=revision_ids,
2228
 
                if_present_ids=if_present_ids, limit=limit)
 
1343
        return repository.InterRepository.get(
 
1344
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
2229
1345
 
2230
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
2231
 
              fetch_spec=None, lossy=False):
 
1346
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
 
1347
            fetch_spec=None):
2232
1348
        # No base implementation to use as RemoteRepository is not a subclass
2233
1349
        # of Repository; so this is a copy of Repository.fetch().
2234
1350
        if fetch_spec is not None and revision_id is not None:
2238
1354
            raise errors.InternalBzrError(
2239
1355
                "May not fetch while in a write group.")
2240
1356
        # fast path same-url fetch operations
2241
 
        if (self.has_same_location(source) and
2242
 
            fetch_spec is None and
2243
 
                self._has_same_fallbacks(source)):
 
1357
        if (self.has_same_location(source)
 
1358
            and fetch_spec is None
 
1359
            and self._has_same_fallbacks(source)):
2244
1360
            # check that last_revision is in 'from' and then return a
2245
1361
            # no-operation.
2246
 
            if (revision_id is not None
2247
 
                    and not _mod_revision.is_null(revision_id)):
 
1362
            if (revision_id is not None and
 
1363
                not revision.is_null(revision_id)):
2248
1364
                self.get_revision(revision_id)
2249
 
            return _mod_repository.FetchResult(0)
 
1365
            return 0, []
2250
1366
        # if there is no specific appropriate InterRepository, this will get
2251
1367
        # the InterRepository base class, which raises an
2252
1368
        # IncompatibleRepositories when asked to fetch.
2253
 
        inter = _mod_repository.InterRepository.get(source, self)
2254
 
        if (fetch_spec is not None
2255
 
                and not getattr(inter, "supports_fetch_spec", False)):
2256
 
            raise errors.UnsupportedOperation(
2257
 
                "fetch_spec not supported for %r" % inter)
2258
 
        return inter.fetch(revision_id=revision_id,
2259
 
                           find_ghosts=find_ghosts, fetch_spec=fetch_spec,
2260
 
                           lossy=lossy)
 
1369
        inter = repository.InterRepository.get(source, self)
 
1370
        return inter.fetch(revision_id=revision_id, pb=pb,
 
1371
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
2261
1372
 
2262
1373
    def create_bundle(self, target, base, fileobj, format=None):
2263
1374
        self._ensure_real()
2264
1375
        self._real_repository.create_bundle(target, base, fileobj, format)
2265
1376
 
 
1377
    @needs_read_lock
 
1378
    def get_ancestry(self, revision_id, topo_sorted=True):
 
1379
        self._ensure_real()
 
1380
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
 
1381
 
2266
1382
    def fileids_altered_by_revision_ids(self, revision_ids):
2267
1383
        self._ensure_real()
2268
1384
        return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
2272
1388
        return self._real_repository._get_versioned_file_checker(
2273
1389
            revisions, revision_versions_cache)
2274
1390
 
2275
 
    def _iter_files_bytes_rpc(self, desired_files, absent):
2276
 
        path = self.controldir._path_for_remote_call(self._client)
2277
 
        lines = []
2278
 
        identifiers = []
2279
 
        for (file_id, revid, identifier) in desired_files:
2280
 
            lines.append(b''.join([
2281
 
                osutils.safe_file_id(file_id),
2282
 
                b'\0',
2283
 
                osutils.safe_revision_id(revid)]))
2284
 
            identifiers.append(identifier)
2285
 
        (response_tuple, response_handler) = (
2286
 
            self._call_with_body_bytes_expecting_body(
2287
 
                b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2288
 
        if response_tuple != (b'ok', ):
2289
 
            response_handler.cancel_read_body()
2290
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2291
 
        byte_stream = response_handler.read_streamed_body()
2292
 
 
2293
 
        def decompress_stream(start, byte_stream, unused):
2294
 
            decompressor = zlib.decompressobj()
2295
 
            yield decompressor.decompress(start)
2296
 
            while decompressor.unused_data == b"":
2297
 
                try:
2298
 
                    data = next(byte_stream)
2299
 
                except StopIteration:
2300
 
                    break
2301
 
                yield decompressor.decompress(data)
2302
 
            yield decompressor.flush()
2303
 
            unused.append(decompressor.unused_data)
2304
 
        unused = b""
2305
 
        while True:
2306
 
            while b"\n" not in unused:
2307
 
                try:
2308
 
                    unused += next(byte_stream)
2309
 
                except StopIteration:
2310
 
                    return
2311
 
            header, rest = unused.split(b"\n", 1)
2312
 
            args = header.split(b"\0")
2313
 
            if args[0] == b"absent":
2314
 
                absent[identifiers[int(args[3])]] = (args[1], args[2])
2315
 
                unused = rest
2316
 
                continue
2317
 
            elif args[0] == b"ok":
2318
 
                idx = int(args[1])
2319
 
            else:
2320
 
                raise errors.UnexpectedSmartServerResponse(args)
2321
 
            unused_chunks = []
2322
 
            yield (identifiers[idx],
2323
 
                   decompress_stream(rest, byte_stream, unused_chunks))
2324
 
            unused = b"".join(unused_chunks)
2325
 
 
2326
1391
    def iter_files_bytes(self, desired_files):
2327
1392
        """See Repository.iter_file_bytes.
2328
1393
        """
2329
 
        try:
2330
 
            absent = {}
2331
 
            for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2332
 
                    desired_files, absent):
2333
 
                yield identifier, bytes_iterator
2334
 
            for fallback in self._fallback_repositories:
2335
 
                if not absent:
2336
 
                    break
2337
 
                desired_files = [(key[0], key[1], identifier)
2338
 
                                 for identifier, key in viewitems(absent)]
2339
 
                for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2340
 
                    del absent[identifier]
2341
 
                    yield identifier, bytes_iterator
2342
 
            if absent:
2343
 
                # There may be more missing items, but raise an exception
2344
 
                # for just one.
2345
 
                missing_identifier = next(iter(absent))
2346
 
                missing_key = absent[missing_identifier]
2347
 
                raise errors.RevisionNotPresent(revision_id=missing_key[1],
2348
 
                                                file_id=missing_key[0])
2349
 
        except errors.UnknownSmartMethod:
2350
 
            self._ensure_real()
2351
 
            for (identifier, bytes_iterator) in (
2352
 
                    self._real_repository.iter_files_bytes(desired_files)):
2353
 
                yield identifier, bytes_iterator
2354
 
 
2355
 
    def get_cached_parent_map(self, revision_ids):
2356
 
        """See breezy.CachingParentsProvider.get_cached_parent_map"""
2357
 
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
 
1394
        self._ensure_real()
 
1395
        return self._real_repository.iter_files_bytes(desired_files)
2358
1396
 
2359
1397
    def get_parent_map(self, revision_ids):
2360
 
        """See breezy.Graph.get_parent_map()."""
 
1398
        """See bzrlib.Graph.get_parent_map()."""
2361
1399
        return self._make_parents_provider().get_parent_map(revision_ids)
2362
1400
 
2363
1401
    def _get_parent_map_rpc(self, keys):
2382
1420
            # There is one other "bug" which is that ghosts in
2383
1421
            # get_revision_graph() are not returned at all. But we won't worry
2384
1422
            # about that for now.
2385
 
            for node_id, parent_ids in viewitems(rg):
 
1423
            for node_id, parent_ids in rg.iteritems():
2386
1424
                if parent_ids == ():
2387
1425
                    rg[node_id] = (NULL_REVISION,)
2388
1426
            rg[NULL_REVISION] = ()
2393
1431
            raise ValueError('get_parent_map(None) is not valid')
2394
1432
        if NULL_REVISION in keys:
2395
1433
            keys.discard(NULL_REVISION)
2396
 
            found_parents = {NULL_REVISION: ()}
 
1434
            found_parents = {NULL_REVISION:()}
2397
1435
            if not keys:
2398
1436
                return found_parents
2399
1437
        else:
2419
1457
        if parents_map is None:
2420
1458
            # Repository is not locked, so there's no cache.
2421
1459
            parents_map = {}
2422
 
        if _DEFAULT_SEARCH_DEPTH <= 0:
2423
 
            (start_set, stop_keys,
2424
 
             key_count) = vf_search.search_result_from_parent_map(
2425
 
                parents_map, self._unstacked_provider.missing_keys)
2426
 
        else:
2427
 
            (start_set, stop_keys,
2428
 
             key_count) = vf_search.limited_search_result_from_parent_map(
2429
 
                parents_map, self._unstacked_provider.missing_keys,
2430
 
                keys, depth=_DEFAULT_SEARCH_DEPTH)
 
1460
        # start_set is all the keys in the cache
 
1461
        start_set = set(parents_map)
 
1462
        # result set is all the references to keys in the cache
 
1463
        result_parents = set()
 
1464
        for parents in parents_map.itervalues():
 
1465
            result_parents.update(parents)
 
1466
        stop_keys = result_parents.difference(start_set)
 
1467
        # We don't need to send ghosts back to the server as a position to
 
1468
        # stop either.
 
1469
        stop_keys.difference_update(self._unstacked_provider.missing_keys)
 
1470
        key_count = len(parents_map)
 
1471
        if (NULL_REVISION in result_parents
 
1472
            and NULL_REVISION in self._unstacked_provider.missing_keys):
 
1473
            # If we pruned NULL_REVISION from the stop_keys because it's also
 
1474
            # in our cache of "missing" keys we need to increment our key count
 
1475
            # by 1, because the reconsitituted SearchResult on the server will
 
1476
            # still consider NULL_REVISION to be an included key.
 
1477
            key_count += 1
 
1478
        included_keys = start_set.intersection(result_parents)
 
1479
        start_set.difference_update(included_keys)
2431
1480
        recipe = ('manual', start_set, stop_keys, key_count)
2432
1481
        body = self._serialise_search_recipe(recipe)
2433
 
        path = self.controldir._path_for_remote_call(self._client)
 
1482
        path = self.bzrdir._path_for_remote_call(self._client)
2434
1483
        for key in keys:
2435
 
            if not isinstance(key, bytes):
 
1484
            if type(key) is not str:
2436
1485
                raise ValueError(
2437
 
                    "key %r not a bytes string" % (key,))
2438
 
        verb = b'Repository.get_parent_map'
2439
 
        args = (path, b'include-missing:') + tuple(keys)
 
1486
                    "key %r not a plain string" % (key,))
 
1487
        verb = 'Repository.get_parent_map'
 
1488
        args = (path, 'include-missing:') + tuple(keys)
2440
1489
        try:
2441
1490
            response = self._call_with_body_bytes_expecting_body(
2442
1491
                verb, args, body)
2455
1504
            # Recurse just once and we should use the fallback code.
2456
1505
            return self._get_parent_map_rpc(keys)
2457
1506
        response_tuple, response_handler = response
2458
 
        if response_tuple[0] not in [b'ok']:
 
1507
        if response_tuple[0] not in ['ok']:
2459
1508
            response_handler.cancel_read_body()
2460
1509
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2461
 
        if response_tuple[0] == b'ok':
 
1510
        if response_tuple[0] == 'ok':
2462
1511
            coded = bz2.decompress(response_handler.read_body_bytes())
2463
 
            if coded == b'':
 
1512
            if coded == '':
2464
1513
                # no revisions found
2465
1514
                return {}
2466
 
            lines = coded.split(b'\n')
 
1515
            lines = coded.split('\n')
2467
1516
            revision_graph = {}
2468
1517
            for line in lines:
2469
1518
                d = tuple(line.split())
2471
1520
                    revision_graph[d[0]] = d[1:]
2472
1521
                else:
2473
1522
                    # No parents:
2474
 
                    if d[0].startswith(b'missing:'):
 
1523
                    if d[0].startswith('missing:'):
2475
1524
                        revid = d[0][8:]
2476
1525
                        self._unstacked_provider.note_missing_key(revid)
2477
1526
                    else:
2480
1529
                        revision_graph[d[0]] = (NULL_REVISION,)
2481
1530
            return revision_graph
2482
1531
 
 
1532
    @needs_read_lock
2483
1533
    def get_signature_text(self, revision_id):
2484
 
        with self.lock_read():
2485
 
            path = self.controldir._path_for_remote_call(self._client)
2486
 
            try:
2487
 
                response_tuple, response_handler = self._call_expecting_body(
2488
 
                    b'Repository.get_revision_signature_text', path, revision_id)
2489
 
            except errors.UnknownSmartMethod:
2490
 
                self._ensure_real()
2491
 
                return self._real_repository.get_signature_text(revision_id)
2492
 
            except errors.NoSuchRevision as err:
2493
 
                for fallback in self._fallback_repositories:
2494
 
                    try:
2495
 
                        return fallback.get_signature_text(revision_id)
2496
 
                    except errors.NoSuchRevision:
2497
 
                        pass
2498
 
                raise err
2499
 
            else:
2500
 
                if response_tuple[0] != b'ok':
2501
 
                    raise errors.UnexpectedSmartServerResponse(response_tuple)
2502
 
                return response_handler.read_body_bytes()
 
1534
        self._ensure_real()
 
1535
        return self._real_repository.get_signature_text(revision_id)
2503
1536
 
 
1537
    @needs_read_lock
2504
1538
    def _get_inventory_xml(self, revision_id):
2505
 
        with self.lock_read():
2506
 
            # This call is used by older working tree formats,
2507
 
            # which stored a serialized basis inventory.
2508
 
            self._ensure_real()
2509
 
            return self._real_repository._get_inventory_xml(revision_id)
 
1539
        self._ensure_real()
 
1540
        return self._real_repository._get_inventory_xml(revision_id)
2510
1541
 
2511
1542
    def reconcile(self, other=None, thorough=False):
2512
 
        from ..reconcile import ReconcileResult
2513
 
        with self.lock_write():
2514
 
            path = self.controldir._path_for_remote_call(self._client)
2515
 
            try:
2516
 
                response, handler = self._call_expecting_body(
2517
 
                    b'Repository.reconcile', path, self._lock_token)
2518
 
            except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2519
 
                self._ensure_real()
2520
 
                return self._real_repository.reconcile(other=other, thorough=thorough)
2521
 
            if response != (b'ok', ):
2522
 
                raise errors.UnexpectedSmartServerResponse(response)
2523
 
            body = handler.read_body_bytes()
2524
 
            result = ReconcileResult()
2525
 
            result.garbage_inventories = None
2526
 
            result.inconsistent_parents = None
2527
 
            result.aborted = None
2528
 
            for line in body.split(b'\n'):
2529
 
                if not line:
2530
 
                    continue
2531
 
                key, val_text = line.split(b':')
2532
 
                if key == b"garbage_inventories":
2533
 
                    result.garbage_inventories = int(val_text)
2534
 
                elif key == b"inconsistent_parents":
2535
 
                    result.inconsistent_parents = int(val_text)
2536
 
                else:
2537
 
                    mutter("unknown reconcile key %r" % key)
2538
 
            return result
 
1543
        self._ensure_real()
 
1544
        return self._real_repository.reconcile(other=other, thorough=thorough)
2539
1545
 
2540
1546
    def all_revision_ids(self):
2541
 
        path = self.controldir._path_for_remote_call(self._client)
2542
 
        try:
2543
 
            response_tuple, response_handler = self._call_expecting_body(
2544
 
                b"Repository.all_revision_ids", path)
2545
 
        except errors.UnknownSmartMethod:
2546
 
            self._ensure_real()
2547
 
            return self._real_repository.all_revision_ids()
2548
 
        if response_tuple != (b"ok", ):
2549
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2550
 
        revids = set(response_handler.read_body_bytes().splitlines())
2551
 
        for fallback in self._fallback_repositories:
2552
 
            revids.update(set(fallback.all_revision_ids()))
2553
 
        return list(revids)
2554
 
 
2555
 
    def _filtered_revision_trees(self, revision_ids, file_ids):
2556
 
        """Return Tree for a revision on this branch with only some files.
2557
 
 
2558
 
        :param revision_ids: a sequence of revision-ids;
2559
 
          a revision-id may not be None or b'null:'
2560
 
        :param file_ids: if not None, the result is filtered
2561
 
          so that only those file-ids, their parents and their
2562
 
          children are included.
2563
 
        """
2564
 
        inventories = self.iter_inventories(revision_ids)
2565
 
        for inv in inventories:
2566
 
            # Should we introduce a FilteredRevisionTree class rather
2567
 
            # than pre-filter the inventory here?
2568
 
            filtered_inv = inv.filter(file_ids)
2569
 
            yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2570
 
 
 
1547
        self._ensure_real()
 
1548
        return self._real_repository.all_revision_ids()
 
1549
 
 
1550
    @needs_read_lock
2571
1551
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2572
 
        with self.lock_read():
2573
 
            medium = self._client._medium
2574
 
            if medium._is_remote_before((1, 2)):
2575
 
                self._ensure_real()
2576
 
                for delta in self._real_repository.get_deltas_for_revisions(
2577
 
                        revisions, specific_fileids):
2578
 
                    yield delta
2579
 
                return
2580
 
            # Get the revision-ids of interest
2581
 
            required_trees = set()
2582
 
            for revision in revisions:
2583
 
                required_trees.add(revision.revision_id)
2584
 
                required_trees.update(revision.parent_ids[:1])
2585
 
 
2586
 
            # Get the matching filtered trees. Note that it's more
2587
 
            # efficient to pass filtered trees to changes_from() rather
2588
 
            # than doing the filtering afterwards. changes_from() could
2589
 
            # arguably do the filtering itself but it's path-based, not
2590
 
            # file-id based, so filtering before or afterwards is
2591
 
            # currently easier.
2592
 
            if specific_fileids is None:
2593
 
                trees = dict((t.get_revision_id(), t) for
2594
 
                             t in self.revision_trees(required_trees))
2595
 
            else:
2596
 
                trees = dict((t.get_revision_id(), t) for
2597
 
                             t in self._filtered_revision_trees(required_trees,
2598
 
                                                                specific_fileids))
2599
 
 
2600
 
            # Calculate the deltas
2601
 
            for revision in revisions:
2602
 
                if not revision.parent_ids:
2603
 
                    old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2604
 
                else:
2605
 
                    old_tree = trees[revision.parent_ids[0]]
2606
 
                yield trees[revision.revision_id].changes_from(old_tree)
2607
 
 
2608
 
    def get_revision_delta(self, revision_id):
2609
 
        with self.lock_read():
2610
 
            r = self.get_revision(revision_id)
2611
 
            return list(self.get_deltas_for_revisions([r]))[0]
2612
 
 
 
1552
        self._ensure_real()
 
1553
        return self._real_repository.get_deltas_for_revisions(revisions,
 
1554
            specific_fileids=specific_fileids)
 
1555
 
 
1556
    @needs_read_lock
 
1557
    def get_revision_delta(self, revision_id, specific_fileids=None):
 
1558
        self._ensure_real()
 
1559
        return self._real_repository.get_revision_delta(revision_id,
 
1560
            specific_fileids=specific_fileids)
 
1561
 
 
1562
    @needs_read_lock
2613
1563
    def revision_trees(self, revision_ids):
2614
 
        with self.lock_read():
2615
 
            inventories = self.iter_inventories(revision_ids)
2616
 
            for inv in inventories:
2617
 
                yield RemoteInventoryTree(self, inv, inv.revision_id)
 
1564
        self._ensure_real()
 
1565
        return self._real_repository.revision_trees(revision_ids)
2618
1566
 
 
1567
    @needs_read_lock
2619
1568
    def get_revision_reconcile(self, revision_id):
2620
 
        with self.lock_read():
2621
 
            self._ensure_real()
2622
 
            return self._real_repository.get_revision_reconcile(revision_id)
 
1569
        self._ensure_real()
 
1570
        return self._real_repository.get_revision_reconcile(revision_id)
2623
1571
 
 
1572
    @needs_read_lock
2624
1573
    def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2625
 
        with self.lock_read():
2626
 
            self._ensure_real()
2627
 
            return self._real_repository.check(revision_ids=revision_ids,
2628
 
                                               callback_refs=callback_refs, check_repo=check_repo)
 
1574
        self._ensure_real()
 
1575
        return self._real_repository.check(revision_ids=revision_ids,
 
1576
            callback_refs=callback_refs, check_repo=check_repo)
2629
1577
 
2630
1578
    def copy_content_into(self, destination, revision_id=None):
2631
 
        """Make a complete copy of the content in self into destination.
2632
 
 
2633
 
        This is a destructive operation! Do not use it on existing
2634
 
        repositories.
2635
 
        """
2636
 
        interrepo = _mod_repository.InterRepository.get(self, destination)
2637
 
        return interrepo.copy_content(revision_id)
 
1579
        self._ensure_real()
 
1580
        return self._real_repository.copy_content_into(
 
1581
            destination, revision_id=revision_id)
2638
1582
 
2639
1583
    def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2640
1584
        # get a tarball of the remote repository, and copy from that into the
2641
1585
        # destination
 
1586
        from bzrlib import osutils
2642
1587
        import tarfile
2643
1588
        # TODO: Maybe a progress bar while streaming the tarball?
2644
 
        note(gettext("Copying repository content as tarball..."))
 
1589
        note("Copying repository content as tarball...")
2645
1590
        tar_file = self._get_tarball('bz2')
2646
1591
        if tar_file is None:
2647
1592
            return None
2648
1593
        destination = to_bzrdir.create_repository()
2649
1594
        try:
2650
1595
            tar = tarfile.open('repository', fileobj=tar_file,
2651
 
                               mode='r|bz2')
 
1596
                mode='r|bz2')
2652
1597
            tmpdir = osutils.mkdtemp()
2653
1598
            try:
2654
 
                tar.extractall(tmpdir)
2655
 
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
 
1599
                _extract_tar(tar, tmpdir)
 
1600
                tmp_bzrdir = BzrDir.open(tmpdir)
2656
1601
                tmp_repo = tmp_bzrdir.open_repository()
2657
1602
                tmp_repo.copy_content_into(destination, revision_id)
2658
1603
            finally:
2673
1618
        self._ensure_real()
2674
1619
        return self._real_repository.inventories
2675
1620
 
 
1621
    @needs_write_lock
2676
1622
    def pack(self, hint=None, clean_obsolete_packs=False):
2677
1623
        """Compress the data within the repository.
 
1624
 
 
1625
        This is not currently implemented within the smart server.
2678
1626
        """
2679
 
        if hint is None:
2680
 
            body = b""
2681
 
        else:
2682
 
            body = b"".join([l.encode('ascii') + b"\n" for l in hint])
2683
 
        with self.lock_write():
2684
 
            path = self.controldir._path_for_remote_call(self._client)
2685
 
            try:
2686
 
                response, handler = self._call_with_body_bytes_expecting_body(
2687
 
                    b'Repository.pack', (path, self._lock_token,
2688
 
                                         str(clean_obsolete_packs).encode('ascii')), body)
2689
 
            except errors.UnknownSmartMethod:
2690
 
                self._ensure_real()
2691
 
                return self._real_repository.pack(hint=hint,
2692
 
                                                  clean_obsolete_packs=clean_obsolete_packs)
2693
 
            handler.cancel_read_body()
2694
 
            if response != (b'ok', ):
2695
 
                raise errors.UnexpectedSmartServerResponse(response)
 
1627
        self._ensure_real()
 
1628
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
2696
1629
 
2697
1630
    @property
2698
1631
    def revisions(self):
2699
1632
        """Decorate the real repository for now.
2700
1633
 
 
1634
        In the short term this should become a real object to intercept graph
 
1635
        lookups.
 
1636
 
2701
1637
        In the long term a full blown network facility is needed.
2702
1638
        """
2703
1639
        self._ensure_real()
2705
1641
 
2706
1642
    def set_make_working_trees(self, new_value):
2707
1643
        if new_value:
2708
 
            new_value_str = b"True"
 
1644
            new_value_str = "True"
2709
1645
        else:
2710
 
            new_value_str = b"False"
2711
 
        path = self.controldir._path_for_remote_call(self._client)
 
1646
            new_value_str = "False"
 
1647
        path = self.bzrdir._path_for_remote_call(self._client)
2712
1648
        try:
2713
1649
            response = self._call(
2714
 
                b'Repository.set_make_working_trees', path, new_value_str)
 
1650
                'Repository.set_make_working_trees', path, new_value_str)
2715
1651
        except errors.UnknownSmartMethod:
2716
1652
            self._ensure_real()
2717
1653
            self._real_repository.set_make_working_trees(new_value)
2718
1654
        else:
2719
 
            if response[0] != b'ok':
 
1655
            if response[0] != 'ok':
2720
1656
                raise errors.UnexpectedSmartServerResponse(response)
2721
1657
 
2722
1658
    @property
2729
1665
        self._ensure_real()
2730
1666
        return self._real_repository.signatures
2731
1667
 
 
1668
    @needs_write_lock
2732
1669
    def sign_revision(self, revision_id, gpg_strategy):
2733
 
        with self.lock_write():
2734
 
            testament = _mod_testament.Testament.from_revision(
2735
 
                self, revision_id)
2736
 
            plaintext = testament.as_short_text()
2737
 
            self.store_revision_signature(gpg_strategy, plaintext, revision_id)
 
1670
        self._ensure_real()
 
1671
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
2738
1672
 
2739
1673
    @property
2740
1674
    def texts(self):
2746
1680
        self._ensure_real()
2747
1681
        return self._real_repository.texts
2748
1682
 
2749
 
    def _iter_revisions_rpc(self, revision_ids):
2750
 
        body = b"\n".join(revision_ids)
2751
 
        path = self.controldir._path_for_remote_call(self._client)
2752
 
        response_tuple, response_handler = (
2753
 
            self._call_with_body_bytes_expecting_body(
2754
 
                b"Repository.iter_revisions", (path, ), body))
2755
 
        if response_tuple[0] != b"ok":
2756
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2757
 
        serializer_format = response_tuple[1].decode('ascii')
2758
 
        serializer = serializer_format_registry.get(serializer_format)
2759
 
        byte_stream = response_handler.read_streamed_body()
2760
 
        decompressor = zlib.decompressobj()
2761
 
        chunks = []
2762
 
        for bytes in byte_stream:
2763
 
            chunks.append(decompressor.decompress(bytes))
2764
 
            if decompressor.unused_data != b"":
2765
 
                chunks.append(decompressor.flush())
2766
 
                yield serializer.read_revision_from_string(b"".join(chunks))
2767
 
                unused = decompressor.unused_data
2768
 
                decompressor = zlib.decompressobj()
2769
 
                chunks = [decompressor.decompress(unused)]
2770
 
        chunks.append(decompressor.flush())
2771
 
        text = b"".join(chunks)
2772
 
        if text != b"":
2773
 
            yield serializer.read_revision_from_string(b"".join(chunks))
2774
 
 
2775
 
    def iter_revisions(self, revision_ids):
2776
 
        for rev_id in revision_ids:
2777
 
            if not rev_id or not isinstance(rev_id, bytes):
2778
 
                raise errors.InvalidRevisionId(
2779
 
                    revision_id=rev_id, branch=self)
2780
 
        with self.lock_read():
2781
 
            try:
2782
 
                missing = set(revision_ids)
2783
 
                for rev in self._iter_revisions_rpc(revision_ids):
2784
 
                    missing.remove(rev.revision_id)
2785
 
                    yield (rev.revision_id, rev)
2786
 
                for fallback in self._fallback_repositories:
2787
 
                    if not missing:
2788
 
                        break
2789
 
                    for (revid, rev) in fallback.iter_revisions(missing):
2790
 
                        if rev is not None:
2791
 
                            yield (revid, rev)
2792
 
                            missing.remove(revid)
2793
 
                for revid in missing:
2794
 
                    yield (revid, None)
2795
 
            except errors.UnknownSmartMethod:
2796
 
                self._ensure_real()
2797
 
                for entry in self._real_repository.iter_revisions(revision_ids):
2798
 
                    yield entry
 
1683
    @needs_read_lock
 
1684
    def get_revisions(self, revision_ids):
 
1685
        self._ensure_real()
 
1686
        return self._real_repository.get_revisions(revision_ids)
2799
1687
 
2800
1688
    def supports_rich_root(self):
2801
1689
        return self._format.rich_root_data
2802
1690
 
 
1691
    def iter_reverse_revision_history(self, revision_id):
 
1692
        self._ensure_real()
 
1693
        return self._real_repository.iter_reverse_revision_history(revision_id)
 
1694
 
2803
1695
    @property
2804
1696
    def _serializer(self):
2805
1697
        return self._format._serializer
2806
1698
 
2807
1699
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2808
 
        with self.lock_write():
2809
 
            signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2810
 
            self.add_signature_text(revision_id, signature)
 
1700
        self._ensure_real()
 
1701
        return self._real_repository.store_revision_signature(
 
1702
            gpg_strategy, plaintext, revision_id)
2811
1703
 
2812
1704
    def add_signature_text(self, revision_id, signature):
2813
 
        if self._real_repository:
2814
 
            # If there is a real repository the write group will
2815
 
            # be in the real repository as well, so use that:
2816
 
            self._ensure_real()
2817
 
            return self._real_repository.add_signature_text(
2818
 
                revision_id, signature)
2819
 
        path = self.controldir._path_for_remote_call(self._client)
2820
 
        response, handler = self._call_with_body_bytes_expecting_body(
2821
 
            b'Repository.add_signature_text', (path, self._lock_token,
2822
 
                                               revision_id) +
2823
 
            tuple([token.encode('utf-8')
2824
 
                   for token in self._write_group_tokens]),
2825
 
            signature)
2826
 
        handler.cancel_read_body()
2827
 
        self.refresh_data()
2828
 
        if response[0] != b'ok':
2829
 
            raise errors.UnexpectedSmartServerResponse(response)
2830
 
        self._write_group_tokens = [token.decode(
2831
 
            'utf-8') for token in response[1:]]
 
1705
        self._ensure_real()
 
1706
        return self._real_repository.add_signature_text(revision_id, signature)
2832
1707
 
2833
1708
    def has_signature_for_revision_id(self, revision_id):
2834
 
        path = self.controldir._path_for_remote_call(self._client)
2835
 
        try:
2836
 
            response = self._call(b'Repository.has_signature_for_revision_id',
2837
 
                                  path, revision_id)
2838
 
        except errors.UnknownSmartMethod:
2839
 
            self._ensure_real()
2840
 
            return self._real_repository.has_signature_for_revision_id(
2841
 
                revision_id)
2842
 
        if response[0] not in (b'yes', b'no'):
2843
 
            raise SmartProtocolError(
2844
 
                'unexpected response code %s' % (response,))
2845
 
        if response[0] == b'yes':
2846
 
            return True
2847
 
        for fallback in self._fallback_repositories:
2848
 
            if fallback.has_signature_for_revision_id(revision_id):
2849
 
                return True
2850
 
        return False
2851
 
 
2852
 
    def verify_revision_signature(self, revision_id, gpg_strategy):
2853
 
        with self.lock_read():
2854
 
            if not self.has_signature_for_revision_id(revision_id):
2855
 
                return gpg.SIGNATURE_NOT_SIGNED, None
2856
 
            signature = self.get_signature_text(revision_id)
2857
 
 
2858
 
            testament = _mod_testament.Testament.from_revision(
2859
 
                self, revision_id)
2860
 
 
2861
 
            (status, key, signed_plaintext) = gpg_strategy.verify(signature)
2862
 
            if testament.as_short_text() != signed_plaintext:
2863
 
                return gpg.SIGNATURE_NOT_VALID, None
2864
 
            return (status, key)
 
1709
        self._ensure_real()
 
1710
        return self._real_repository.has_signature_for_revision_id(revision_id)
2865
1711
 
2866
1712
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2867
1713
        self._ensure_real()
2868
1714
        return self._real_repository.item_keys_introduced_by(revision_ids,
2869
 
                                                             _files_pb=_files_pb)
 
1715
            _files_pb=_files_pb)
 
1716
 
 
1717
    def revision_graph_can_have_wrong_parents(self):
 
1718
        # The answer depends on the remote repo format.
 
1719
        self._ensure_real()
 
1720
        return self._real_repository.revision_graph_can_have_wrong_parents()
2870
1721
 
2871
1722
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2872
1723
        self._ensure_real()
2881
1732
        providers = [self._unstacked_provider]
2882
1733
        if other is not None:
2883
1734
            providers.insert(0, other)
2884
 
        return graph.StackedParentsProvider(_LazyListJoin(
2885
 
            providers, self._fallback_repositories))
 
1735
        providers.extend(r._make_parents_provider() for r in
 
1736
                         self._fallback_repositories)
 
1737
        return graph.StackedParentsProvider(providers)
2886
1738
 
2887
1739
    def _serialise_search_recipe(self, recipe):
2888
1740
        """Serialise a graph search recipe.
2890
1742
        :param recipe: A search recipe (start, stop, count).
2891
1743
        :return: Serialised bytes.
2892
1744
        """
2893
 
        start_keys = b' '.join(recipe[1])
2894
 
        stop_keys = b' '.join(recipe[2])
2895
 
        count = str(recipe[3]).encode('ascii')
2896
 
        return b'\n'.join((start_keys, stop_keys, count))
 
1745
        start_keys = ' '.join(recipe[1])
 
1746
        stop_keys = ' '.join(recipe[2])
 
1747
        count = str(recipe[3])
 
1748
        return '\n'.join((start_keys, stop_keys, count))
2897
1749
 
2898
1750
    def _serialise_search_result(self, search_result):
2899
 
        parts = search_result.get_network_struct()
2900
 
        return b'\n'.join(parts)
 
1751
        if isinstance(search_result, graph.PendingAncestryResult):
 
1752
            parts = ['ancestry-of']
 
1753
            parts.extend(search_result.heads)
 
1754
        else:
 
1755
            recipe = search_result.get_recipe()
 
1756
            parts = [recipe[0], self._serialise_search_recipe(recipe)]
 
1757
        return '\n'.join(parts)
2901
1758
 
2902
1759
    def autopack(self):
2903
 
        path = self.controldir._path_for_remote_call(self._client)
 
1760
        path = self.bzrdir._path_for_remote_call(self._client)
2904
1761
        try:
2905
 
            response = self._call(b'PackRepository.autopack', path)
 
1762
            response = self._call('PackRepository.autopack', path)
2906
1763
        except errors.UnknownSmartMethod:
2907
1764
            self._ensure_real()
2908
1765
            self._real_repository._pack_collection.autopack()
2909
1766
            return
2910
1767
        self.refresh_data()
2911
 
        if response[0] != b'ok':
2912
 
            raise errors.UnexpectedSmartServerResponse(response)
2913
 
 
2914
 
    def _revision_archive(self, revision_id, format, name, root, subdir,
2915
 
                          force_mtime=None):
2916
 
        path = self.controldir._path_for_remote_call(self._client)
2917
 
        format = format or ''
2918
 
        root = root or ''
2919
 
        subdir = subdir or ''
2920
 
        force_mtime = int(force_mtime) if force_mtime is not None else None
2921
 
        try:
2922
 
            response, protocol = self._call_expecting_body(
2923
 
                b'Repository.revision_archive', path,
2924
 
                revision_id,
2925
 
                format.encode('ascii'),
2926
 
                os.path.basename(name).encode('utf-8'),
2927
 
                root.encode('utf-8'),
2928
 
                subdir.encode('utf-8'),
2929
 
                force_mtime)
2930
 
        except errors.UnknownSmartMethod:
2931
 
            return None
2932
 
        if response[0] == b'ok':
2933
 
            return iter([protocol.read_body_bytes()])
2934
 
        raise errors.UnexpectedSmartServerResponse(response)
2935
 
 
2936
 
    def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2937
 
        path = self.controldir._path_for_remote_call(self._client)
2938
 
        tree_path = tree_path.encode('utf-8')
2939
 
        file_id = file_id or b''
2940
 
        default_revision = default_revision or b''
2941
 
        try:
2942
 
            response, handler = self._call_expecting_body(
2943
 
                b'Repository.annotate_file_revision', path,
2944
 
                revid, tree_path, file_id, default_revision)
2945
 
        except errors.UnknownSmartMethod:
2946
 
            return None
2947
 
        if response[0] != b'ok':
2948
 
            raise errors.UnexpectedSmartServerResponse(response)
2949
 
        return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2950
 
 
2951
 
 
2952
 
class RemoteStreamSink(vf_repository.StreamSink):
 
1768
        if response[0] != 'ok':
 
1769
            raise errors.UnexpectedSmartServerResponse(response)
 
1770
 
 
1771
 
 
1772
class RemoteStreamSink(repository.StreamSink):
2953
1773
 
2954
1774
    def _insert_real(self, stream, src_format, resume_tokens):
2955
1775
        self.target_repo._ensure_real()
2959
1779
            self.target_repo.autopack()
2960
1780
        return result
2961
1781
 
2962
 
    def insert_missing_keys(self, source, missing_keys):
2963
 
        if (isinstance(source, RemoteStreamSource)
2964
 
                and source.from_repository._client._medium == self.target_repo._client._medium):
2965
 
            # Streaming from and to the same medium is tricky, since we don't support
2966
 
            # more than one concurrent request. For now, just force VFS.
2967
 
            stream = source._get_real_stream_for_missing_keys(missing_keys)
2968
 
        else:
2969
 
            stream = source.get_stream_for_missing_keys(missing_keys)
2970
 
        return self.insert_stream_without_locking(stream,
2971
 
                                                  self.target_repo._format)
2972
 
 
2973
1782
    def insert_stream(self, stream, src_format, resume_tokens):
2974
1783
        target = self.target_repo
2975
1784
        target._unstacked_provider.missing_keys.clear()
2976
 
        candidate_calls = [(b'Repository.insert_stream_1.19', (1, 19))]
 
1785
        candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
2977
1786
        if target._lock_token:
2978
 
            candidate_calls.append(
2979
 
                (b'Repository.insert_stream_locked', (1, 14)))
2980
 
            lock_args = (target._lock_token or b'',)
 
1787
            candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
 
1788
            lock_args = (target._lock_token or '',)
2981
1789
        else:
2982
 
            candidate_calls.append((b'Repository.insert_stream', (1, 13)))
 
1790
            candidate_calls.append(('Repository.insert_stream', (1, 13)))
2983
1791
            lock_args = ()
2984
1792
        client = target._client
2985
1793
        medium = client._medium
2986
 
        path = target.controldir._path_for_remote_call(client)
 
1794
        path = target.bzrdir._path_for_remote_call(client)
2987
1795
        # Probe for the verb to use with an empty stream before sending the
2988
1796
        # real stream to it.  We do this both to avoid the risk of sending a
2989
1797
        # large request that is then rejected, and because we don't want to
3000
1808
            byte_stream = smart_repo._stream_to_byte_stream([], src_format)
3001
1809
            try:
3002
1810
                response = client.call_with_body_stream(
3003
 
                    (verb, path, b'') + lock_args, byte_stream)
 
1811
                    (verb, path, '') + lock_args, byte_stream)
3004
1812
            except errors.UnknownSmartMethod:
3005
1813
                medium._remember_remote_is_before(required_version)
3006
1814
            else:
3019
1827
            stream = self._stop_stream_if_inventory_delta(stream)
3020
1828
        byte_stream = smart_repo._stream_to_byte_stream(
3021
1829
            stream, src_format)
3022
 
        resume_tokens = b' '.join([token.encode('utf-8')
3023
 
                                   for token in resume_tokens])
 
1830
        resume_tokens = ' '.join(resume_tokens)
3024
1831
        response = client.call_with_body_stream(
3025
1832
            (verb, path, resume_tokens) + lock_args, byte_stream)
3026
 
        if response[0][0] not in (b'ok', b'missing-basis'):
 
1833
        if response[0][0] not in ('ok', 'missing-basis'):
3027
1834
            raise errors.UnexpectedSmartServerResponse(response)
3028
1835
        if self._last_substream is not None:
3029
1836
            # The stream included an inventory-delta record, but the remote
3031
1838
            # rest of the stream via VFS.
3032
1839
            self.target_repo.refresh_data()
3033
1840
            return self._resume_stream_with_vfs(response, src_format)
3034
 
        if response[0][0] == b'missing-basis':
 
1841
        if response[0][0] == 'missing-basis':
3035
1842
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
3036
 
            resume_tokens = [token.decode('utf-8') for token in tokens]
3037
 
            return resume_tokens, set((entry[0].decode('utf-8'), ) + entry[1:] for entry in missing_keys)
 
1843
            resume_tokens = tokens
 
1844
            return resume_tokens, set(missing_keys)
3038
1845
        else:
3039
1846
            self.target_repo.refresh_data()
3040
1847
            return [], set()
3043
1850
        """Resume sending a stream via VFS, first resending the record and
3044
1851
        substream that couldn't be sent via an insert_stream verb.
3045
1852
        """
3046
 
        if response[0][0] == b'missing-basis':
 
1853
        if response[0][0] == 'missing-basis':
3047
1854
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
3048
 
            tokens = [token.decode('utf-8') for token in tokens]
3049
1855
            # Ignore missing_keys, we haven't finished inserting yet
3050
1856
        else:
3051
1857
            tokens = []
3052
 
 
3053
1858
        def resume_substream():
3054
1859
            # Yield the substream that was interrupted.
3055
1860
            for record in self._last_substream:
3056
1861
                yield record
3057
1862
            self._last_substream = None
3058
 
 
3059
1863
        def resume_stream():
3060
1864
            # Finish sending the interrupted substream
3061
1865
            yield ('inventory-deltas', resume_substream())
3072
1876
        self._last_substream and self._last_stream so that the stream can be
3073
1877
        resumed by _resume_stream_with_vfs.
3074
1878
        """
3075
 
 
 
1879
                    
3076
1880
        stream_iter = iter(stream)
3077
1881
        for substream_kind, substream in stream_iter:
3078
1882
            if substream_kind == 'inventory-deltas':
3081
1885
                return
3082
1886
            else:
3083
1887
                yield substream_kind, substream
3084
 
 
3085
 
 
3086
 
class RemoteStreamSource(vf_repository.StreamSource):
 
1888
            
 
1889
 
 
1890
class RemoteStreamSource(repository.StreamSource):
3087
1891
    """Stream data from a remote server."""
3088
1892
 
3089
1893
    def get_stream(self, search):
3090
 
        if (self.from_repository._fallback_repositories
3091
 
                and self.to_format._fetch_order == 'topological'):
 
1894
        if (self.from_repository._fallback_repositories and
 
1895
            self.to_format._fetch_order == 'topological'):
3092
1896
            return self._real_stream(self.from_repository, search)
3093
1897
        sources = []
3094
1898
        seen = set()
3102
1906
            sources.append(repo)
3103
1907
        return self.missing_parents_chain(search, sources)
3104
1908
 
3105
 
    def _get_real_stream_for_missing_keys(self, missing_keys):
 
1909
    def get_stream_for_missing_keys(self, missing_keys):
3106
1910
        self.from_repository._ensure_real()
3107
1911
        real_repo = self.from_repository._real_repository
3108
1912
        real_source = real_repo._get_source(self.to_format)
3109
1913
        return real_source.get_stream_for_missing_keys(missing_keys)
3110
1914
 
3111
 
    def get_stream_for_missing_keys(self, missing_keys):
3112
 
        if not isinstance(self.from_repository, RemoteRepository):
3113
 
            return self._get_real_stream_for_missing_keys(missing_keys)
3114
 
        client = self.from_repository._client
3115
 
        medium = client._medium
3116
 
        if medium._is_remote_before((3, 0)):
3117
 
            return self._get_real_stream_for_missing_keys(missing_keys)
3118
 
        path = self.from_repository.controldir._path_for_remote_call(client)
3119
 
        args = (path, self.to_format.network_name())
3120
 
        search_bytes = b'\n'.join(
3121
 
            [b'%s\t%s' % (key[0].encode('utf-8'), key[1]) for key in missing_keys])
3122
 
        try:
3123
 
            response, handler = self.from_repository._call_with_body_bytes_expecting_body(
3124
 
                b'Repository.get_stream_for_missing_keys', args, search_bytes)
3125
 
        except (errors.UnknownSmartMethod, errors.UnknownFormatError):
3126
 
            return self._get_real_stream_for_missing_keys(missing_keys)
3127
 
        if response[0] != b'ok':
3128
 
            raise errors.UnexpectedSmartServerResponse(response)
3129
 
        byte_stream = handler.read_streamed_body()
3130
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
3131
 
                                                               self._record_counter)
3132
 
        if src_format.network_name() != self.from_repository._format.network_name():
3133
 
            raise AssertionError(
3134
 
                "Mismatched RemoteRepository and stream src %r, %r" % (
3135
 
                    src_format.network_name(), repo._format.network_name()))
3136
 
        return stream
3137
 
 
3138
1915
    def _real_stream(self, repo, search):
3139
1916
        """Get a stream for search from repo.
3140
 
 
3141
 
        This never called RemoteStreamSource.get_stream, and is a helper
3142
 
        for RemoteStreamSource._get_stream to allow getting a stream
 
1917
        
 
1918
        This never called RemoteStreamSource.get_stream, and is a heler
 
1919
        for RemoteStreamSource._get_stream to allow getting a stream 
3143
1920
        reliably whether fallback back because of old servers or trying
3144
1921
        to stream from a non-RemoteRepository (which the stacked support
3145
1922
        code will do).
3170
1947
            return self._real_stream(repo, search)
3171
1948
        client = repo._client
3172
1949
        medium = client._medium
3173
 
        path = repo.controldir._path_for_remote_call(client)
 
1950
        path = repo.bzrdir._path_for_remote_call(client)
3174
1951
        search_bytes = repo._serialise_search_result(search)
3175
1952
        args = (path, self.to_format.network_name())
3176
1953
        candidate_verbs = [
3177
 
            (b'Repository.get_stream_1.19', (1, 19)),
3178
 
            (b'Repository.get_stream', (1, 13))]
3179
 
 
 
1954
            ('Repository.get_stream_1.19', (1, 19)),
 
1955
            ('Repository.get_stream', (1, 13))]
3180
1956
        found_verb = False
3181
1957
        for verb, version in candidate_verbs:
3182
1958
            if medium._is_remote_before(version):
3186
1962
                    verb, args, search_bytes)
3187
1963
            except errors.UnknownSmartMethod:
3188
1964
                medium._remember_remote_is_before(version)
3189
 
            except errors.UnknownErrorFromSmartServer as e:
3190
 
                if isinstance(search, vf_search.EverythingResult):
3191
 
                    error_verb = e.error_from_smart_server.error_verb
3192
 
                    if error_verb == b'BadSearch':
3193
 
                        # Pre-2.4 servers don't support this sort of search.
3194
 
                        # XXX: perhaps falling back to VFS on BadSearch is a
3195
 
                        # good idea in general?  It might provide a little bit
3196
 
                        # of protection against client-side bugs.
3197
 
                        medium._remember_remote_is_before((2, 4))
3198
 
                        break
3199
 
                raise
3200
1965
            else:
3201
1966
                response_tuple, response_handler = response
3202
1967
                found_verb = True
3203
1968
                break
3204
1969
        if not found_verb:
3205
1970
            return self._real_stream(repo, search)
3206
 
        if response_tuple[0] != b'ok':
 
1971
        if response_tuple[0] != 'ok':
3207
1972
            raise errors.UnexpectedSmartServerResponse(response_tuple)
3208
1973
        byte_stream = response_handler.read_streamed_body()
3209
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
3210
 
                                                               self._record_counter)
 
1974
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
3211
1975
        if src_format.network_name() != repo._format.network_name():
3212
1976
            raise AssertionError(
3213
1977
                "Mismatched RemoteRepository and stream src %r, %r" % (
3214
 
                    src_format.network_name(), repo._format.network_name()))
 
1978
                src_format.network_name(), repo._format.network_name()))
3215
1979
        return stream
3216
1980
 
3217
1981
    def missing_parents_chain(self, search, sources):
3257
2021
    """
3258
2022
 
3259
2023
    def __init__(self, bzrdir, _client):
3260
 
        self.controldir = bzrdir
 
2024
        self.bzrdir = bzrdir
3261
2025
        self._client = _client
3262
2026
        self._need_find_modes = True
3263
2027
        LockableFiles.__init__(
3274
2038
 
3275
2039
    def __init__(self, network_name=None):
3276
2040
        super(RemoteBranchFormat, self).__init__()
3277
 
        self._matchingcontroldir = RemoteBzrDirFormat()
3278
 
        self._matchingcontroldir.set_branch_format(self)
 
2041
        self._matchingbzrdir = RemoteBzrDirFormat()
 
2042
        self._matchingbzrdir.set_branch_format(self)
3279
2043
        self._custom_format = None
3280
2044
        self._network_name = network_name
3281
2045
 
3282
2046
    def __eq__(self, other):
3283
 
        return (isinstance(other, RemoteBranchFormat)
3284
 
                and self.__dict__ == other.__dict__)
 
2047
        return (isinstance(other, RemoteBranchFormat) and
 
2048
            self.__dict__ == other.__dict__)
3285
2049
 
3286
2050
    def _ensure_real(self):
3287
2051
        if self._custom_format is None:
3288
 
            try:
3289
 
                self._custom_format = branch.network_format_registry.get(
3290
 
                    self._network_name)
3291
 
            except KeyError:
3292
 
                raise errors.UnknownFormatError(kind='branch',
3293
 
                                                format=self._network_name)
 
2052
            self._custom_format = branch.network_format_registry.get(
 
2053
                self._network_name)
3294
2054
 
3295
2055
    def get_format_description(self):
3296
2056
        self._ensure_real()
3299
2059
    def network_name(self):
3300
2060
        return self._network_name
3301
2061
 
3302
 
    def open(self, a_controldir, name=None, ignore_fallbacks=False):
3303
 
        return a_controldir.open_branch(name=name,
3304
 
                                        ignore_fallbacks=ignore_fallbacks)
 
2062
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
 
2063
        return a_bzrdir.open_branch(name=name, 
 
2064
            ignore_fallbacks=ignore_fallbacks)
3305
2065
 
3306
 
    def _vfs_initialize(self, a_controldir, name, append_revisions_only,
3307
 
                        repository=None):
 
2066
    def _vfs_initialize(self, a_bzrdir, name):
3308
2067
        # Initialisation when using a local bzrdir object, or a non-vfs init
3309
2068
        # method is not available on the server.
3310
2069
        # self._custom_format is always set - the start of initialize ensures
3311
2070
        # that.
3312
 
        if isinstance(a_controldir, RemoteBzrDir):
3313
 
            a_controldir._ensure_real()
3314
 
            result = self._custom_format.initialize(a_controldir._real_bzrdir,
3315
 
                                                    name=name, append_revisions_only=append_revisions_only,
3316
 
                                                    repository=repository)
 
2071
        if isinstance(a_bzrdir, RemoteBzrDir):
 
2072
            a_bzrdir._ensure_real()
 
2073
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
 
2074
                name)
3317
2075
        else:
3318
2076
            # We assume the bzrdir is parameterised; it may not be.
3319
 
            result = self._custom_format.initialize(a_controldir, name=name,
3320
 
                                                    append_revisions_only=append_revisions_only,
3321
 
                                                    repository=repository)
3322
 
        if (isinstance(a_controldir, RemoteBzrDir)
3323
 
                and not isinstance(result, RemoteBranch)):
3324
 
            result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
 
2077
            result = self._custom_format.initialize(a_bzrdir, name)
 
2078
        if (isinstance(a_bzrdir, RemoteBzrDir) and
 
2079
            not isinstance(result, RemoteBranch)):
 
2080
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3325
2081
                                  name=name)
3326
2082
        return result
3327
2083
 
3328
 
    def initialize(self, a_controldir, name=None, repository=None,
3329
 
                   append_revisions_only=None):
3330
 
        if name is None:
3331
 
            name = a_controldir._get_selected_branch()
 
2084
    def initialize(self, a_bzrdir, name=None):
3332
2085
        # 1) get the network name to use.
3333
2086
        if self._custom_format:
3334
2087
            network_name = self._custom_format.network_name()
3335
2088
        else:
3336
 
            # Select the current breezy default and ask for that.
3337
 
            reference_bzrdir_format = controldir.format_registry.get(
3338
 
                'default')()
 
2089
            # Select the current bzrlib default and ask for that.
 
2090
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
3339
2091
            reference_format = reference_bzrdir_format.get_branch_format()
3340
2092
            self._custom_format = reference_format
3341
2093
            network_name = reference_format.network_name()
3342
2094
        # Being asked to create on a non RemoteBzrDir:
3343
 
        if not isinstance(a_controldir, RemoteBzrDir):
3344
 
            return self._vfs_initialize(a_controldir, name=name,
3345
 
                                        append_revisions_only=append_revisions_only,
3346
 
                                        repository=repository)
3347
 
        medium = a_controldir._client._medium
 
2095
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
2096
            return self._vfs_initialize(a_bzrdir, name=name)
 
2097
        medium = a_bzrdir._client._medium
3348
2098
        if medium._is_remote_before((1, 13)):
3349
 
            return self._vfs_initialize(a_controldir, name=name,
3350
 
                                        append_revisions_only=append_revisions_only,
3351
 
                                        repository=repository)
 
2099
            return self._vfs_initialize(a_bzrdir, name=name)
3352
2100
        # Creating on a remote bzr dir.
3353
2101
        # 2) try direct creation via RPC
3354
 
        path = a_controldir._path_for_remote_call(a_controldir._client)
3355
 
        if name != "":
 
2102
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
2103
        if name is not None:
3356
2104
            # XXX JRV20100304: Support creating colocated branches
3357
2105
            raise errors.NoColocatedBranchSupport(self)
3358
 
        verb = b'BzrDir.create_branch'
 
2106
        verb = 'BzrDir.create_branch'
3359
2107
        try:
3360
 
            response = a_controldir._call(verb, path, network_name)
 
2108
            response = a_bzrdir._call(verb, path, network_name)
3361
2109
        except errors.UnknownSmartMethod:
3362
2110
            # Fallback - use vfs methods
3363
2111
            medium._remember_remote_is_before((1, 13))
3364
 
            return self._vfs_initialize(a_controldir, name=name,
3365
 
                                        append_revisions_only=append_revisions_only,
3366
 
                                        repository=repository)
3367
 
        if response[0] != b'ok':
 
2112
            return self._vfs_initialize(a_bzrdir, name=name)
 
2113
        if response[0] != 'ok':
3368
2114
            raise errors.UnexpectedSmartServerResponse(response)
3369
2115
        # Turn the response into a RemoteRepository object.
3370
2116
        format = RemoteBranchFormat(network_name=response[1])
3371
2117
        repo_format = response_tuple_to_repo_format(response[3:])
3372
 
        repo_path = response[2].decode('utf-8')
3373
 
        if repository is not None:
3374
 
            remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3375
 
            url_diff = urlutils.relative_url(repository.user_url,
3376
 
                                             remote_repo_url)
3377
 
            if url_diff != '.':
3378
 
                raise AssertionError(
3379
 
                    'repository.user_url %r does not match URL from server '
3380
 
                    'response (%r + %r)'
3381
 
                    % (repository.user_url, a_controldir.user_url, repo_path))
3382
 
            remote_repo = repository
 
2118
        if response[2] == '':
 
2119
            repo_bzrdir = a_bzrdir
3383
2120
        else:
3384
 
            if repo_path == '':
3385
 
                repo_bzrdir = a_controldir
3386
 
            else:
3387
 
                repo_bzrdir = RemoteBzrDir(
3388
 
                    a_controldir.root_transport.clone(
3389
 
                        repo_path), a_controldir._format,
3390
 
                    a_controldir._client)
3391
 
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3392
 
        remote_branch = RemoteBranch(a_controldir, remote_repo,
3393
 
                                     format=format, setup_stacking=False, name=name)
3394
 
        if append_revisions_only:
3395
 
            remote_branch.set_append_revisions_only(append_revisions_only)
 
2121
            repo_bzrdir = RemoteBzrDir(
 
2122
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
 
2123
                a_bzrdir._client)
 
2124
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
2125
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
 
2126
            format=format, setup_stacking=False, name=name)
3396
2127
        # XXX: We know this is a new branch, so it must have revno 0, revid
3397
2128
        # NULL_REVISION. Creating the branch locked would make this be unable
3398
2129
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3417
2148
        self._ensure_real()
3418
2149
        return self._custom_format.supports_set_append_revisions_only()
3419
2150
 
3420
 
    @property
3421
 
    def supports_reference_locations(self):
3422
 
        self._ensure_real()
3423
 
        return self._custom_format.supports_reference_locations
3424
 
 
3425
 
    def stores_revno(self):
3426
 
        return True
3427
 
 
3428
 
    def _use_default_local_heads_to_fetch(self):
3429
 
        # If the branch format is a metadir format *and* its heads_to_fetch
3430
 
        # implementation is not overridden vs the base class, we can use the
3431
 
        # base class logic rather than use the heads_to_fetch RPC.  This is
3432
 
        # usually cheaper in terms of net round trips, as the last-revision and
3433
 
        # tags info fetched is cached and would be fetched anyway.
3434
 
        self._ensure_real()
3435
 
        if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3436
 
            branch_class = self._custom_format._branch_class()
3437
 
            heads_to_fetch_impl = get_unbound_function(
3438
 
                branch_class.heads_to_fetch)
3439
 
            if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3440
 
                return True
3441
 
        return False
3442
 
 
3443
 
 
3444
 
class RemoteBranchStore(_mod_config.IniFileStore):
3445
 
    """Branch store which attempts to use HPSS calls to retrieve branch store.
3446
 
 
3447
 
    Note that this is specific to bzr-based formats.
3448
 
    """
3449
 
 
3450
 
    def __init__(self, branch):
3451
 
        super(RemoteBranchStore, self).__init__()
3452
 
        self.branch = branch
3453
 
        self.id = "branch"
3454
 
        self._real_store = None
3455
 
 
3456
 
    def external_url(self):
3457
 
        return urlutils.join(self.branch.user_url, 'branch.conf')
3458
 
 
3459
 
    def _load_content(self):
3460
 
        path = self.branch._remote_path()
3461
 
        try:
3462
 
            response, handler = self.branch._call_expecting_body(
3463
 
                b'Branch.get_config_file', path)
3464
 
        except errors.UnknownSmartMethod:
3465
 
            self._ensure_real()
3466
 
            return self._real_store._load_content()
3467
 
        if len(response) and response[0] != b'ok':
3468
 
            raise errors.UnexpectedSmartServerResponse(response)
3469
 
        return handler.read_body_bytes()
3470
 
 
3471
 
    def _save_content(self, content):
3472
 
        path = self.branch._remote_path()
3473
 
        try:
3474
 
            response, handler = self.branch._call_with_body_bytes_expecting_body(
3475
 
                b'Branch.put_config_file', (path,
3476
 
                                            self.branch._lock_token, self.branch._repo_lock_token),
3477
 
                content)
3478
 
        except errors.UnknownSmartMethod:
3479
 
            self._ensure_real()
3480
 
            return self._real_store._save_content(content)
3481
 
        handler.cancel_read_body()
3482
 
        if response != (b'ok', ):
3483
 
            raise errors.UnexpectedSmartServerResponse(response)
3484
 
 
3485
 
    def _ensure_real(self):
3486
 
        self.branch._ensure_real()
3487
 
        if self._real_store is None:
3488
 
            self._real_store = _mod_config.BranchStore(self.branch)
3489
 
 
3490
2151
 
3491
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3492
2153
    """Branch stored on a server accessed by HPSS RPC.
3495
2156
    """
3496
2157
 
3497
2158
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
3498
 
                 _client=None, format=None, setup_stacking=True, name=None,
3499
 
                 possible_transports=None):
 
2159
        _client=None, format=None, setup_stacking=True, name=None):
3500
2160
        """Create a RemoteBranch instance.
3501
2161
 
3502
2162
        :param real_branch: An optional local implementation of the branch
3513
2173
        # We intentionally don't call the parent class's __init__, because it
3514
2174
        # will try to assign to self.tags, which is a property in this subclass.
3515
2175
        # And the parent's __init__ doesn't do much anyway.
3516
 
        self.controldir = remote_bzrdir
3517
 
        self.name = name
 
2176
        self.bzrdir = remote_bzrdir
3518
2177
        if _client is not None:
3519
2178
            self._client = _client
3520
2179
        else:
3532
2191
            self._real_branch.repository = self.repository
3533
2192
        else:
3534
2193
            self._real_branch = None
3535
 
        # Fill out expected attributes of branch for breezy API users.
 
2194
        # Fill out expected attributes of branch for bzrlib API users.
3536
2195
        self._clear_cached_state()
3537
2196
        # TODO: deprecate self.base in favor of user_url
3538
 
        self.base = self.controldir.user_url
 
2197
        self.base = self.bzrdir.user_url
3539
2198
        self._name = name
3540
2199
        self._control_files = None
3541
2200
        self._lock_mode = None
3543
2202
        self._repo_lock_token = None
3544
2203
        self._lock_count = 0
3545
2204
        self._leave_lock = False
3546
 
        self.conf_store = None
3547
2205
        # Setup a format: note that we cannot call _ensure_real until all the
3548
2206
        # attributes above are set: This code cannot be moved higher up in this
3549
2207
        # function.
3569
2227
            hook(self)
3570
2228
        self._is_stacked = False
3571
2229
        if setup_stacking:
3572
 
            self._setup_stacking(possible_transports)
 
2230
            self._setup_stacking()
3573
2231
 
3574
 
    def _setup_stacking(self, possible_transports):
 
2232
    def _setup_stacking(self):
3575
2233
        # configure stacking into the remote repository, by reading it from
3576
2234
        # the vfs branch.
3577
2235
        try:
3578
2236
            fallback_url = self.get_stacked_on_url()
3579
 
        except (errors.NotStacked, branch.UnstackableBranchFormat,
3580
 
                errors.UnstackableRepositoryFormat) as e:
 
2237
        except (errors.NotStacked, errors.UnstackableBranchFormat,
 
2238
            errors.UnstackableRepositoryFormat), e:
3581
2239
            return
3582
2240
        self._is_stacked = True
3583
 
        if possible_transports is None:
3584
 
            possible_transports = []
3585
 
        else:
3586
 
            possible_transports = list(possible_transports)
3587
 
        possible_transports.append(self.controldir.root_transport)
3588
 
        self._activate_fallback_location(fallback_url,
3589
 
                                         possible_transports=possible_transports)
 
2241
        self._activate_fallback_location(fallback_url)
3590
2242
 
3591
2243
    def _get_config(self):
3592
2244
        return RemoteBranchConfig(self)
3593
2245
 
3594
 
    def _get_config_store(self):
3595
 
        if self.conf_store is None:
3596
 
            self.conf_store = RemoteBranchStore(self)
3597
 
        return self.conf_store
3598
 
 
3599
 
    def store_uncommitted(self, creator):
3600
 
        self._ensure_real()
3601
 
        return self._real_branch.store_uncommitted(creator)
3602
 
 
3603
 
    def get_unshelver(self, tree):
3604
 
        self._ensure_real()
3605
 
        return self._real_branch.get_unshelver(tree)
3606
 
 
3607
2246
    def _get_real_transport(self):
3608
2247
        # if we try vfs access, return the real branch's vfs transport
3609
2248
        self._ensure_real()
3624
2263
        if self._real_branch is None:
3625
2264
            if not vfs.vfs_enabled():
3626
2265
                raise AssertionError('smart server vfs must be enabled '
3627
 
                                     'to use vfs implementation')
3628
 
            self.controldir._ensure_real()
3629
 
            self._real_branch = self.controldir._real_bzrdir.open_branch(
 
2266
                    'to use vfs implementation')
 
2267
            self.bzrdir._ensure_real()
 
2268
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
3630
2269
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
3631
 
            # The remote branch and the real branch shares the same store. If
3632
 
            # we don't, there will always be cases where one of the stores
3633
 
            # doesn't see an update made on the other.
3634
 
            self._real_branch.conf_store = self.conf_store
3635
2270
            if self.repository._real_repository is None:
3636
2271
                # Give the remote repository the matching real repo.
3637
2272
                real_repo = self._real_branch.repository
3652
2287
 
3653
2288
    def _clear_cached_state(self):
3654
2289
        super(RemoteBranch, self)._clear_cached_state()
3655
 
        self._tags_bytes = None
3656
2290
        if self._real_branch is not None:
3657
2291
            self._real_branch._clear_cached_state()
3658
2292
 
3674
2308
        # because it triggers an _ensure_real that we otherwise might not need.
3675
2309
        if self._control_files is None:
3676
2310
            self._control_files = RemoteBranchLockableFiles(
3677
 
                self.controldir, self._client)
 
2311
                self.bzrdir, self._client)
3678
2312
        return self._control_files
3679
2313
 
 
2314
    def _get_checkout_format(self):
 
2315
        self._ensure_real()
 
2316
        return self._real_branch._get_checkout_format()
 
2317
 
3680
2318
    def get_physical_lock_status(self):
3681
2319
        """See Branch.get_physical_lock_status()."""
3682
 
        try:
3683
 
            response = self._client.call(b'Branch.get_physical_lock_status',
3684
 
                                         self._remote_path())
3685
 
        except errors.UnknownSmartMethod:
3686
 
            self._ensure_real()
3687
 
            return self._real_branch.get_physical_lock_status()
3688
 
        if response[0] not in (b'yes', b'no'):
3689
 
            raise errors.UnexpectedSmartServerResponse(response)
3690
 
        return (response[0] == b'yes')
 
2320
        # should be an API call to the server, as branches must be lockable.
 
2321
        self._ensure_real()
 
2322
        return self._real_branch.get_physical_lock_status()
3691
2323
 
3692
2324
    def get_stacked_on_url(self):
3693
2325
        """Get the URL this branch is stacked against.
3701
2333
        try:
3702
2334
            # there may not be a repository yet, so we can't use
3703
2335
            # self._translate_error, so we can't use self._call either.
3704
 
            response = self._client.call(b'Branch.get_stacked_on_url',
3705
 
                                         self._remote_path())
3706
 
        except errors.ErrorFromSmartServer as err:
 
2336
            response = self._client.call('Branch.get_stacked_on_url',
 
2337
                self._remote_path())
 
2338
        except errors.ErrorFromSmartServer, err:
3707
2339
            # there may not be a repository yet, so we can't call through
3708
2340
            # its _translate_error
3709
2341
            _translate_error(err, branch=self)
3710
 
        except errors.UnknownSmartMethod as err:
 
2342
        except errors.UnknownSmartMethod, err:
3711
2343
            self._ensure_real()
3712
2344
            return self._real_branch.get_stacked_on_url()
3713
 
        if response[0] != b'ok':
 
2345
        if response[0] != 'ok':
3714
2346
            raise errors.UnexpectedSmartServerResponse(response)
3715
 
        if sys.version_info[0] == 3:
3716
 
            return response[1].decode('utf-8')
3717
2347
        return response[1]
3718
2348
 
3719
2349
    def set_stacked_on_url(self, url):
3720
2350
        branch.Branch.set_stacked_on_url(self, url)
3721
 
        # We need the stacked_on_url to be visible both locally (to not query
3722
 
        # it repeatedly) and remotely (so smart verbs can get it server side)
3723
 
        # Without the following line,
3724
 
        # breezy.tests.per_branch.test_create_clone.TestCreateClone
3725
 
        # .test_create_clone_on_transport_stacked_hooks_get_stacked_branch
3726
 
        # fails for remote branches -- vila 2012-01-04
3727
 
        self.conf_store.save_changes()
3728
2351
        if not url:
3729
2352
            self._is_stacked = False
3730
2353
        else:
3731
2354
            self._is_stacked = True
3732
 
 
 
2355
        
3733
2356
    def _vfs_get_tags_bytes(self):
3734
2357
        self._ensure_real()
3735
2358
        return self._real_branch._get_tags_bytes()
3736
2359
 
3737
2360
    def _get_tags_bytes(self):
3738
 
        with self.lock_read():
3739
 
            if self._tags_bytes is None:
3740
 
                self._tags_bytes = self._get_tags_bytes_via_hpss()
3741
 
            return self._tags_bytes
3742
 
 
3743
 
    def _get_tags_bytes_via_hpss(self):
3744
2361
        medium = self._client._medium
3745
2362
        if medium._is_remote_before((1, 13)):
3746
2363
            return self._vfs_get_tags_bytes()
3747
2364
        try:
3748
 
            response = self._call(
3749
 
                b'Branch.get_tags_bytes', self._remote_path())
 
2365
            response = self._call('Branch.get_tags_bytes', self._remote_path())
3750
2366
        except errors.UnknownSmartMethod:
3751
2367
            medium._remember_remote_is_before((1, 13))
3752
2368
            return self._vfs_get_tags_bytes()
3757
2373
        return self._real_branch._set_tags_bytes(bytes)
3758
2374
 
3759
2375
    def _set_tags_bytes(self, bytes):
3760
 
        if self.is_locked():
3761
 
            self._tags_bytes = bytes
3762
2376
        medium = self._client._medium
3763
2377
        if medium._is_remote_before((1, 18)):
3764
2378
            self._vfs_set_tags_bytes(bytes)
3767
2381
            args = (
3768
2382
                self._remote_path(), self._lock_token, self._repo_lock_token)
3769
2383
            response = self._call_with_body_bytes(
3770
 
                b'Branch.set_tags_bytes', args, bytes)
 
2384
                'Branch.set_tags_bytes', args, bytes)
3771
2385
        except errors.UnknownSmartMethod:
3772
2386
            medium._remember_remote_is_before((1, 18))
3773
2387
            self._vfs_set_tags_bytes(bytes)
3774
2388
 
3775
2389
    def lock_read(self):
3776
 
        """Lock the branch for read operations.
3777
 
 
3778
 
        :return: A breezy.lock.LogicalLockResult.
3779
 
        """
3780
2390
        self.repository.lock_read()
3781
2391
        if not self._lock_mode:
3782
2392
            self._note_lock('r')
3786
2396
                self._real_branch.lock_read()
3787
2397
        else:
3788
2398
            self._lock_count += 1
3789
 
        return lock.LogicalLockResult(self.unlock)
3790
2399
 
3791
2400
    def _remote_lock_write(self, token):
3792
2401
        if token is None:
3793
 
            branch_token = repo_token = b''
 
2402
            branch_token = repo_token = ''
3794
2403
        else:
3795
2404
            branch_token = token
3796
 
            repo_token = self.repository.lock_write().repository_token
 
2405
            repo_token = self.repository.lock_write()
3797
2406
            self.repository.unlock()
3798
2407
        err_context = {'token': token}
3799
 
        try:
3800
 
            response = self._call(
3801
 
                b'Branch.lock_write', self._remote_path(), branch_token,
3802
 
                repo_token or b'', **err_context)
3803
 
        except errors.LockContention as e:
3804
 
            # The LockContention from the server doesn't have any
3805
 
            # information about the lock_url. We re-raise LockContention
3806
 
            # with valid lock_url.
3807
 
            raise errors.LockContention('(remote lock)',
3808
 
                                        self.repository.base.split('.bzr/')[0])
3809
 
        if response[0] != b'ok':
 
2408
        response = self._call(
 
2409
            'Branch.lock_write', self._remote_path(), branch_token,
 
2410
            repo_token or '', **err_context)
 
2411
        if response[0] != 'ok':
3810
2412
            raise errors.UnexpectedSmartServerResponse(response)
3811
2413
        ok, branch_token, repo_token = response
3812
2414
        return branch_token, repo_token
3818
2420
            remote_tokens = self._remote_lock_write(token)
3819
2421
            self._lock_token, self._repo_lock_token = remote_tokens
3820
2422
            if not self._lock_token:
3821
 
                raise SmartProtocolError(
3822
 
                    'Remote server did not return a token!')
 
2423
                raise SmartProtocolError('Remote server did not return a token!')
3823
2424
            # Tell the self.repository object that it is locked.
3824
2425
            self.repository.lock_write(
3825
2426
                self._repo_lock_token, _skip_rpc=True)
3833
2434
            self._lock_mode = 'w'
3834
2435
            self._lock_count = 1
3835
2436
        elif self._lock_mode == 'r':
3836
 
            raise errors.ReadOnlyError(self)
 
2437
            raise errors.ReadOnlyTransaction
3837
2438
        else:
3838
2439
            if token is not None:
3839
2440
                # A token was given to lock_write, and we're relocking, so
3844
2445
            self._lock_count += 1
3845
2446
            # Re-lock the repository too.
3846
2447
            self.repository.lock_write(self._repo_lock_token)
3847
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2448
        return self._lock_token or None
3848
2449
 
3849
2450
    def _unlock(self, branch_token, repo_token):
3850
2451
        err_context = {'token': str((branch_token, repo_token))}
3851
2452
        response = self._call(
3852
 
            b'Branch.unlock', self._remote_path(), branch_token,
3853
 
            repo_token or b'', **err_context)
3854
 
        if response == (b'ok',):
 
2453
            'Branch.unlock', self._remote_path(), branch_token,
 
2454
            repo_token or '', **err_context)
 
2455
        if response == ('ok',):
3855
2456
            return
3856
2457
        raise errors.UnexpectedSmartServerResponse(response)
3857
2458
 
3860
2461
        try:
3861
2462
            self._lock_count -= 1
3862
2463
            if not self._lock_count:
3863
 
                if self.conf_store is not None:
3864
 
                    self.conf_store.save_changes()
3865
2464
                self._clear_cached_state()
3866
2465
                mode = self._lock_mode
3867
2466
                self._lock_mode = None
3868
2467
                if self._real_branch is not None:
3869
 
                    if (not self._leave_lock and mode == 'w'
3870
 
                            and self._repo_lock_token):
 
2468
                    if (not self._leave_lock and mode == 'w' and
 
2469
                        self._repo_lock_token):
3871
2470
                        # If this RemoteBranch will remove the physical lock
3872
2471
                        # for the repository, make sure the _real_branch
3873
2472
                        # doesn't do it first.  (Because the _real_branch's
3890
2489
            self.repository.unlock()
3891
2490
 
3892
2491
    def break_lock(self):
3893
 
        try:
3894
 
            response = self._call(
3895
 
                b'Branch.break_lock', self._remote_path())
3896
 
        except errors.UnknownSmartMethod:
3897
 
            self._ensure_real()
3898
 
            return self._real_branch.break_lock()
3899
 
        if response != (b'ok',):
3900
 
            raise errors.UnexpectedSmartServerResponse(response)
 
2492
        self._ensure_real()
 
2493
        return self._real_branch.break_lock()
3901
2494
 
3902
2495
    def leave_lock_in_place(self):
3903
2496
        if not self._lock_token:
3909
2502
            raise NotImplementedError(self.dont_leave_lock_in_place)
3910
2503
        self._leave_lock = False
3911
2504
 
 
2505
    @needs_read_lock
3912
2506
    def get_rev_id(self, revno, history=None):
3913
2507
        if revno == 0:
3914
2508
            return _mod_revision.NULL_REVISION
3915
 
        with self.lock_read():
3916
 
            last_revision_info = self.last_revision_info()
3917
 
            if revno < 0:
3918
 
                raise errors.RevnoOutOfBounds(
3919
 
                    revno, (0, last_revision_info[0]))
3920
 
            ok, result = self.repository.get_rev_id_for_revno(
3921
 
                revno, last_revision_info)
3922
 
            if ok:
3923
 
                return result
3924
 
            missing_parent = result[1]
3925
 
            # Either the revision named by the server is missing, or its parent
3926
 
            # is.  Call get_parent_map to determine which, so that we report a
3927
 
            # useful error.
3928
 
            parent_map = self.repository.get_parent_map([missing_parent])
3929
 
            if missing_parent in parent_map:
3930
 
                missing_parent = parent_map[missing_parent]
3931
 
            raise errors.NoSuchRevision(self, missing_parent)
 
2509
        last_revision_info = self.last_revision_info()
 
2510
        ok, result = self.repository.get_rev_id_for_revno(
 
2511
            revno, last_revision_info)
 
2512
        if ok:
 
2513
            return result
 
2514
        missing_parent = result[1]
 
2515
        # Either the revision named by the server is missing, or its parent
 
2516
        # is.  Call get_parent_map to determine which, so that we report a
 
2517
        # useful error.
 
2518
        parent_map = self.repository.get_parent_map([missing_parent])
 
2519
        if missing_parent in parent_map:
 
2520
            missing_parent = parent_map[missing_parent]
 
2521
        raise errors.RevisionNotPresent(missing_parent, self.repository)
3932
2522
 
3933
 
    def _read_last_revision_info(self):
3934
 
        response = self._call(
3935
 
            b'Branch.last_revision_info', self._remote_path())
3936
 
        if response[0] != b'ok':
3937
 
            raise SmartProtocolError(
3938
 
                'unexpected response code %s' % (response,))
 
2523
    def _last_revision_info(self):
 
2524
        response = self._call('Branch.last_revision_info', self._remote_path())
 
2525
        if response[0] != 'ok':
 
2526
            raise SmartProtocolError('unexpected response code %s' % (response,))
3939
2527
        revno = int(response[1])
3940
2528
        last_revision = response[2]
3941
2529
        return (revno, last_revision)
3946
2534
            self._ensure_real()
3947
2535
            return self._real_branch._gen_revision_history()
3948
2536
        response_tuple, response_handler = self._call_expecting_body(
3949
 
            b'Branch.revision_history', self._remote_path())
3950
 
        if response_tuple[0] != b'ok':
 
2537
            'Branch.revision_history', self._remote_path())
 
2538
        if response_tuple[0] != 'ok':
3951
2539
            raise errors.UnexpectedSmartServerResponse(response_tuple)
3952
 
        result = response_handler.read_body_bytes().split(b'\x00')
 
2540
        result = response_handler.read_body_bytes().split('\x00')
3953
2541
        if result == ['']:
3954
2542
            return []
3955
2543
        return result
3956
2544
 
3957
2545
    def _remote_path(self):
3958
 
        return self.controldir._path_for_remote_call(self._client)
 
2546
        return self.bzrdir._path_for_remote_call(self._client)
3959
2547
 
3960
2548
    def _set_last_revision_descendant(self, revision_id, other_branch,
3961
 
                                      allow_diverged=False, allow_overwrite_descendant=False):
 
2549
            allow_diverged=False, allow_overwrite_descendant=False):
3962
2550
        # This performs additional work to meet the hook contract; while its
3963
2551
        # undesirable, we have to synthesise the revno to call the hook, and
3964
2552
        # not calling the hook is worse as it means changes can't be prevented.
3969
2557
        history = self._lefthand_history(revision_id)
3970
2558
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
3971
2559
        err_context = {'other_branch': other_branch}
3972
 
        response = self._call(b'Branch.set_last_revision_ex',
3973
 
                              self._remote_path(), self._lock_token, self._repo_lock_token,
3974
 
                              revision_id, int(allow_diverged), int(
3975
 
                                  allow_overwrite_descendant),
3976
 
                              **err_context)
 
2560
        response = self._call('Branch.set_last_revision_ex',
 
2561
            self._remote_path(), self._lock_token, self._repo_lock_token,
 
2562
            revision_id, int(allow_diverged), int(allow_overwrite_descendant),
 
2563
            **err_context)
3977
2564
        self._clear_cached_state()
3978
 
        if len(response) != 3 and response[0] != b'ok':
 
2565
        if len(response) != 3 and response[0] != 'ok':
3979
2566
            raise errors.UnexpectedSmartServerResponse(response)
3980
2567
        new_revno, new_revision_id = response[1:]
3981
2568
        self._last_revision_info_cache = new_revno, new_revision_id
3995
2582
        history = self._lefthand_history(revision_id)
3996
2583
        self._run_pre_change_branch_tip_hooks(len(history), revision_id)
3997
2584
        self._clear_cached_state()
3998
 
        response = self._call(b'Branch.set_last_revision',
3999
 
                              self._remote_path(), self._lock_token, self._repo_lock_token,
4000
 
                              revision_id)
4001
 
        if response != (b'ok',):
 
2585
        response = self._call('Branch.set_last_revision',
 
2586
            self._remote_path(), self._lock_token, self._repo_lock_token,
 
2587
            revision_id)
 
2588
        if response != ('ok',):
4002
2589
            raise errors.UnexpectedSmartServerResponse(response)
4003
2590
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4004
2591
 
 
2592
    @needs_write_lock
 
2593
    def set_revision_history(self, rev_history):
 
2594
        # Send just the tip revision of the history; the server will generate
 
2595
        # the full history from that.  If the revision doesn't exist in this
 
2596
        # branch, NoSuchRevision will be raised.
 
2597
        if rev_history == []:
 
2598
            rev_id = 'null:'
 
2599
        else:
 
2600
            rev_id = rev_history[-1]
 
2601
        self._set_last_revision(rev_id)
 
2602
        for hook in branch.Branch.hooks['set_rh']:
 
2603
            hook(self, rev_history)
 
2604
        self._cache_revision_history(rev_history)
 
2605
 
4005
2606
    def _get_parent_location(self):
4006
2607
        medium = self._client._medium
4007
2608
        if medium._is_remote_before((1, 13)):
4008
2609
            return self._vfs_get_parent_location()
4009
2610
        try:
4010
 
            response = self._call(b'Branch.get_parent', self._remote_path())
 
2611
            response = self._call('Branch.get_parent', self._remote_path())
4011
2612
        except errors.UnknownSmartMethod:
4012
2613
            medium._remember_remote_is_before((1, 13))
4013
2614
            return self._vfs_get_parent_location()
4014
2615
        if len(response) != 1:
4015
2616
            raise errors.UnexpectedSmartServerResponse(response)
4016
2617
        parent_location = response[0]
4017
 
        if parent_location == b'':
 
2618
        if parent_location == '':
4018
2619
            return None
4019
 
        return parent_location.decode('utf-8')
 
2620
        return parent_location
4020
2621
 
4021
2622
    def _vfs_get_parent_location(self):
4022
2623
        self._ensure_real()
4027
2628
        if medium._is_remote_before((1, 15)):
4028
2629
            return self._vfs_set_parent_location(url)
4029
2630
        try:
4030
 
            call_url = url or u''
4031
 
            if isinstance(call_url, text_type):
4032
 
                call_url = call_url.encode('utf-8')
4033
 
            response = self._call(b'Branch.set_parent_location',
4034
 
                                  self._remote_path(), self._lock_token, self._repo_lock_token,
4035
 
                                  call_url)
 
2631
            call_url = url or ''
 
2632
            if type(call_url) is not str:
 
2633
                raise AssertionError('url must be a str or None (%s)' % url)
 
2634
            response = self._call('Branch.set_parent_location',
 
2635
                self._remote_path(), self._lock_token, self._repo_lock_token,
 
2636
                call_url)
4036
2637
        except errors.UnknownSmartMethod:
4037
2638
            medium._remember_remote_is_before((1, 15))
4038
2639
            return self._vfs_set_parent_location(url)
4043
2644
        self._ensure_real()
4044
2645
        return self._real_branch._set_parent_location(url)
4045
2646
 
 
2647
    @needs_write_lock
4046
2648
    def pull(self, source, overwrite=False, stop_revision=None,
4047
2649
             **kwargs):
4048
 
        with self.lock_write():
4049
 
            self._clear_cached_state_of_remote_branch_only()
4050
 
            self._ensure_real()
4051
 
            return self._real_branch.pull(
4052
 
                source, overwrite=overwrite, stop_revision=stop_revision,
4053
 
                _override_hook_target=self, **kwargs)
4054
 
 
4055
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False, tag_selector=None):
4056
 
        with self.lock_read():
4057
 
            self._ensure_real()
4058
 
            return self._real_branch.push(
4059
 
                target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
4060
 
                _override_hook_source_branch=self, tag_selector=tag_selector)
4061
 
 
4062
 
    def peek_lock_mode(self):
4063
 
        return self._lock_mode
 
2650
        self._clear_cached_state_of_remote_branch_only()
 
2651
        self._ensure_real()
 
2652
        return self._real_branch.pull(
 
2653
            source, overwrite=overwrite, stop_revision=stop_revision,
 
2654
            _override_hook_target=self, **kwargs)
 
2655
 
 
2656
    @needs_read_lock
 
2657
    def push(self, target, overwrite=False, stop_revision=None):
 
2658
        self._ensure_real()
 
2659
        return self._real_branch.push(
 
2660
            target, overwrite=overwrite, stop_revision=stop_revision,
 
2661
            _override_hook_source_branch=self)
4064
2662
 
4065
2663
    def is_locked(self):
4066
2664
        return self._lock_count >= 1
4067
2665
 
4068
 
    def revision_id_to_dotted_revno(self, revision_id):
4069
 
        """Given a revision id, return its dotted revno.
4070
 
 
4071
 
        :return: a tuple like (1,) or (400,1,3).
4072
 
        """
4073
 
        with self.lock_read():
4074
 
            try:
4075
 
                response = self._call(b'Branch.revision_id_to_revno',
4076
 
                                      self._remote_path(), revision_id)
4077
 
            except errors.UnknownSmartMethod:
4078
 
                self._ensure_real()
4079
 
                return self._real_branch.revision_id_to_dotted_revno(revision_id)
4080
 
            except errors.UnknownErrorFromSmartServer as e:
4081
 
                # Deal with older versions of bzr/brz that didn't explicitly
4082
 
                # wrap GhostRevisionsHaveNoRevno.
4083
 
                if e.error_tuple[1] == b'GhostRevisionsHaveNoRevno':
4084
 
                    (revid, ghost_revid) = re.findall(b"{([^}]+)}", e.error_tuple[2])
4085
 
                    raise errors.GhostRevisionsHaveNoRevno(
4086
 
                        revid, ghost_revid)
4087
 
                raise
4088
 
            if response[0] == b'ok':
4089
 
                return tuple([int(x) for x in response[1:]])
4090
 
            else:
4091
 
                raise errors.UnexpectedSmartServerResponse(response)
4092
 
 
 
2666
    @needs_read_lock
4093
2667
    def revision_id_to_revno(self, revision_id):
4094
 
        """Given a revision id on the branch mainline, return its revno.
4095
 
 
4096
 
        :return: an integer
4097
 
        """
4098
 
        with self.lock_read():
4099
 
            try:
4100
 
                response = self._call(b'Branch.revision_id_to_revno',
4101
 
                                      self._remote_path(), revision_id)
4102
 
            except errors.UnknownSmartMethod:
4103
 
                self._ensure_real()
4104
 
                return self._real_branch.revision_id_to_revno(revision_id)
4105
 
            if response[0] == b'ok':
4106
 
                if len(response) == 2:
4107
 
                    return int(response[1])
4108
 
                raise NoSuchRevision(self, revision_id)
4109
 
            else:
4110
 
                raise errors.UnexpectedSmartServerResponse(response)
4111
 
 
 
2668
        self._ensure_real()
 
2669
        return self._real_branch.revision_id_to_revno(revision_id)
 
2670
 
 
2671
    @needs_write_lock
4112
2672
    def set_last_revision_info(self, revno, revision_id):
4113
 
        with self.lock_write():
4114
 
            # XXX: These should be returned by the set_last_revision_info verb
4115
 
            old_revno, old_revid = self.last_revision_info()
4116
 
            self._run_pre_change_branch_tip_hooks(revno, revision_id)
4117
 
            if not revision_id or not isinstance(revision_id, bytes):
4118
 
                raise errors.InvalidRevisionId(
4119
 
                    revision_id=revision_id, branch=self)
4120
 
            try:
4121
 
                response = self._call(b'Branch.set_last_revision_info',
4122
 
                                      self._remote_path(), self._lock_token, self._repo_lock_token,
4123
 
                                      str(revno).encode('ascii'), revision_id)
4124
 
            except errors.UnknownSmartMethod:
4125
 
                self._ensure_real()
4126
 
                self._clear_cached_state_of_remote_branch_only()
4127
 
                self._real_branch.set_last_revision_info(revno, revision_id)
4128
 
                self._last_revision_info_cache = revno, revision_id
4129
 
                return
4130
 
            if response == (b'ok',):
4131
 
                self._clear_cached_state()
4132
 
                self._last_revision_info_cache = revno, revision_id
4133
 
                self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4134
 
                # Update the _real_branch's cache too.
4135
 
                if self._real_branch is not None:
4136
 
                    cache = self._last_revision_info_cache
4137
 
                    self._real_branch._last_revision_info_cache = cache
4138
 
            else:
4139
 
                raise errors.UnexpectedSmartServerResponse(response)
 
2673
        # XXX: These should be returned by the set_last_revision_info verb
 
2674
        old_revno, old_revid = self.last_revision_info()
 
2675
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2676
        revision_id = ensure_null(revision_id)
 
2677
        try:
 
2678
            response = self._call('Branch.set_last_revision_info',
 
2679
                self._remote_path(), self._lock_token, self._repo_lock_token,
 
2680
                str(revno), revision_id)
 
2681
        except errors.UnknownSmartMethod:
 
2682
            self._ensure_real()
 
2683
            self._clear_cached_state_of_remote_branch_only()
 
2684
            self._real_branch.set_last_revision_info(revno, revision_id)
 
2685
            self._last_revision_info_cache = revno, revision_id
 
2686
            return
 
2687
        if response == ('ok',):
 
2688
            self._clear_cached_state()
 
2689
            self._last_revision_info_cache = revno, revision_id
 
2690
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2691
            # Update the _real_branch's cache too.
 
2692
            if self._real_branch is not None:
 
2693
                cache = self._last_revision_info_cache
 
2694
                self._real_branch._last_revision_info_cache = cache
 
2695
        else:
 
2696
            raise errors.UnexpectedSmartServerResponse(response)
4140
2697
 
 
2698
    @needs_write_lock
4141
2699
    def generate_revision_history(self, revision_id, last_rev=None,
4142
2700
                                  other_branch=None):
4143
 
        with self.lock_write():
4144
 
            medium = self._client._medium
4145
 
            if not medium._is_remote_before((1, 6)):
4146
 
                # Use a smart method for 1.6 and above servers
4147
 
                try:
4148
 
                    self._set_last_revision_descendant(revision_id, other_branch,
4149
 
                                                       allow_diverged=True, allow_overwrite_descendant=True)
4150
 
                    return
4151
 
                except errors.UnknownSmartMethod:
4152
 
                    medium._remember_remote_is_before((1, 6))
4153
 
            self._clear_cached_state_of_remote_branch_only()
4154
 
            graph = self.repository.get_graph()
4155
 
            (last_revno, last_revid) = self.last_revision_info()
4156
 
            known_revision_ids = [
4157
 
                (last_revid, last_revno),
4158
 
                (_mod_revision.NULL_REVISION, 0),
4159
 
                ]
4160
 
            if last_rev is not None:
4161
 
                if not graph.is_ancestor(last_rev, revision_id):
4162
 
                    # our previous tip is not merged into stop_revision
4163
 
                    raise errors.DivergedBranches(self, other_branch)
4164
 
            revno = graph.find_distance_to_null(
4165
 
                revision_id, known_revision_ids)
4166
 
            self.set_last_revision_info(revno, revision_id)
 
2701
        medium = self._client._medium
 
2702
        if not medium._is_remote_before((1, 6)):
 
2703
            # Use a smart method for 1.6 and above servers
 
2704
            try:
 
2705
                self._set_last_revision_descendant(revision_id, other_branch,
 
2706
                    allow_diverged=True, allow_overwrite_descendant=True)
 
2707
                return
 
2708
            except errors.UnknownSmartMethod:
 
2709
                medium._remember_remote_is_before((1, 6))
 
2710
        self._clear_cached_state_of_remote_branch_only()
 
2711
        self.set_revision_history(self._lefthand_history(revision_id,
 
2712
            last_rev=last_rev,other_branch=other_branch))
4167
2713
 
4168
2714
    def set_push_location(self, location):
4169
 
        self._set_config_location('push_location', location)
4170
 
 
4171
 
    def heads_to_fetch(self):
4172
 
        if self._format._use_default_local_heads_to_fetch():
4173
 
            # We recognise this format, and its heads-to-fetch implementation
4174
 
            # is the default one (tip + tags).  In this case it's cheaper to
4175
 
            # just use the default implementation rather than a special RPC as
4176
 
            # the tip and tags data is cached.
4177
 
            return branch.Branch.heads_to_fetch(self)
4178
 
        medium = self._client._medium
4179
 
        if medium._is_remote_before((2, 4)):
4180
 
            return self._vfs_heads_to_fetch()
4181
 
        try:
4182
 
            return self._rpc_heads_to_fetch()
4183
 
        except errors.UnknownSmartMethod:
4184
 
            medium._remember_remote_is_before((2, 4))
4185
 
            return self._vfs_heads_to_fetch()
4186
 
 
4187
 
    def _rpc_heads_to_fetch(self):
4188
 
        response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4189
 
        if len(response) != 2:
4190
 
            raise errors.UnexpectedSmartServerResponse(response)
4191
 
        must_fetch, if_present_fetch = response
4192
 
        return set(must_fetch), set(if_present_fetch)
4193
 
 
4194
 
    def _vfs_heads_to_fetch(self):
4195
 
        self._ensure_real()
4196
 
        return self._real_branch.heads_to_fetch()
4197
 
 
4198
 
    def reconcile(self, thorough=True):
4199
 
        """Make sure the data stored in this branch is consistent."""
4200
 
        from .reconcile import BranchReconciler
4201
 
        with self.lock_write():
4202
 
            reconciler = BranchReconciler(self, thorough=thorough)
4203
 
            return reconciler.reconcile()
4204
 
 
4205
 
    def get_reference_info(self, file_id):
4206
 
        """Get the tree_path and branch_location for a tree reference."""
4207
 
        if not self._format.supports_reference_locations:
4208
 
            raise errors.UnsupportedOperation(self.get_reference_info, self)
4209
 
        return self._get_all_reference_info().get(file_id, (None, None))
4210
 
 
4211
 
    def set_reference_info(self, file_id, branch_location, tree_path=None):
4212
 
        """Set the branch location to use for a tree reference."""
4213
 
        if not self._format.supports_reference_locations:
4214
 
            raise errors.UnsupportedOperation(self.set_reference_info, self)
4215
 
        self._ensure_real()
4216
 
        self._real_branch.set_reference_info(
4217
 
            file_id, branch_location, tree_path)
4218
 
 
4219
 
    def _set_all_reference_info(self, reference_info):
4220
 
        if not self._format.supports_reference_locations:
4221
 
            raise errors.UnsupportedOperation(self.set_reference_info, self)
4222
 
        self._ensure_real()
4223
 
        self._real_branch._set_all_reference_info(reference_info)
4224
 
 
4225
 
    def _get_all_reference_info(self):
4226
 
        if not self._format.supports_reference_locations:
4227
 
            return {}
4228
 
        try:
4229
 
            response, handler = self._call_expecting_body(
4230
 
                b'Branch.get_all_reference_info', self._remote_path())
4231
 
        except errors.UnknownSmartMethod:
4232
 
            self._ensure_real()
4233
 
            return self._real_branch._get_all_reference_info()
4234
 
        if len(response) and response[0] != b'ok':
4235
 
            raise errors.UnexpectedSmartServerResponse(response)
4236
 
        ret = {}
4237
 
        for (f, u, p) in bencode.bdecode(handler.read_body_bytes()):
4238
 
            ret[f] = (u.decode('utf-8'), p.decode('utf-8') if p else None)
4239
 
        return ret
4240
 
 
4241
 
    def reference_parent(self, file_id, path, possible_transports=None):
4242
 
        """Return the parent branch for a tree-reference.
4243
 
 
4244
 
        :param path: The path of the nested tree in the tree
4245
 
        :return: A branch associated with the nested tree
4246
 
        """
4247
 
        branch_location = self.get_reference_info(file_id)[0]
4248
 
        if branch_location is None:
4249
 
            try:
4250
 
                return branch.Branch.open_from_transport(
4251
 
                    self.controldir.root_transport.clone(path),
4252
 
                    possible_transports=possible_transports)
4253
 
            except errors.NotBranchError:
4254
 
                return None
4255
 
        return branch.Branch.open(
4256
 
            urlutils.join(
4257
 
                urlutils.strip_segment_parameters(self.user_url), branch_location),
4258
 
            possible_transports=possible_transports)
 
2715
        self._ensure_real()
 
2716
        return self._real_branch.set_push_location(location)
4259
2717
 
4260
2718
 
4261
2719
class RemoteConfig(object):
4263
2721
 
4264
2722
    It is a low-level object that considers config data to be name/value pairs
4265
2723
    that may be associated with a section. Assigning meaning to the these
4266
 
    values is done at higher levels like breezy.config.TreeConfig.
 
2724
    values is done at higher levels like bzrlib.config.TreeConfig.
4267
2725
    """
4268
2726
 
4269
2727
    def get_option(self, name, section=None, default=None):
4276
2734
        """
4277
2735
        try:
4278
2736
            configobj = self._get_configobj()
4279
 
            section_obj = None
4280
2737
            if section is None:
4281
2738
                section_obj = configobj
4282
2739
            else:
4283
2740
                try:
4284
2741
                    section_obj = configobj[section]
4285
2742
                except KeyError:
4286
 
                    pass
4287
 
            if section_obj is None:
4288
 
                value = default
4289
 
            else:
4290
 
                value = section_obj.get(name, default)
 
2743
                    return default
 
2744
            return section_obj.get(name, default)
4291
2745
        except errors.UnknownSmartMethod:
4292
 
            value = self._vfs_get_option(name, section, default)
4293
 
        for hook in _mod_config.OldConfigHooks['get']:
4294
 
            hook(self, name, value)
4295
 
        return value
 
2746
            return self._vfs_get_option(name, section, default)
4296
2747
 
4297
2748
    def _response_to_configobj(self, response):
4298
 
        if len(response[0]) and response[0][0] != b'ok':
 
2749
        if len(response[0]) and response[0][0] != 'ok':
4299
2750
            raise errors.UnexpectedSmartServerResponse(response)
4300
2751
        lines = response[1].read_body_bytes().splitlines()
4301
 
        conf = _mod_config.ConfigObj(lines, encoding='utf-8')
4302
 
        for hook in _mod_config.OldConfigHooks['load']:
4303
 
            hook(self)
4304
 
        return conf
 
2752
        return config.ConfigObj(lines, encoding='utf-8')
4305
2753
 
4306
2754
 
4307
2755
class RemoteBranchConfig(RemoteConfig):
4313
2761
    def _get_configobj(self):
4314
2762
        path = self._branch._remote_path()
4315
2763
        response = self._branch._client.call_expecting_body(
4316
 
            b'Branch.get_config_file', path)
 
2764
            'Branch.get_config_file', path)
4317
2765
        return self._response_to_configobj(response)
4318
2766
 
4319
2767
    def set_option(self, value, name, section=None):
4326
2774
        medium = self._branch._client._medium
4327
2775
        if medium._is_remote_before((1, 14)):
4328
2776
            return self._vfs_set_option(value, name, section)
4329
 
        if isinstance(value, dict):
4330
 
            if medium._is_remote_before((2, 2)):
4331
 
                return self._vfs_set_option(value, name, section)
4332
 
            return self._set_config_option_dict(value, name, section)
4333
 
        else:
4334
 
            return self._set_config_option(value, name, section)
4335
 
 
4336
 
    def _set_config_option(self, value, name, section):
4337
 
        if isinstance(value, (bool, int)):
4338
 
            value = str(value)
4339
 
        elif isinstance(value, (text_type, str)):
4340
 
            pass
4341
 
        else:
4342
 
            raise TypeError(value)
4343
2777
        try:
4344
2778
            path = self._branch._remote_path()
4345
 
            response = self._branch._client.call(b'Branch.set_config_option',
4346
 
                                                 path, self._branch._lock_token, self._branch._repo_lock_token,
4347
 
                                                 value.encode('utf-8'), name.encode('utf-8'),
4348
 
                                                 (section or '').encode('utf-8'))
 
2779
            response = self._branch._client.call('Branch.set_config_option',
 
2780
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
2781
                value.encode('utf8'), name, section or '')
4349
2782
        except errors.UnknownSmartMethod:
4350
 
            medium = self._branch._client._medium
4351
2783
            medium._remember_remote_is_before((1, 14))
4352
2784
            return self._vfs_set_option(value, name, section)
4353
2785
        if response != ():
4354
2786
            raise errors.UnexpectedSmartServerResponse(response)
4355
2787
 
4356
 
    def _serialize_option_dict(self, option_dict):
4357
 
        utf8_dict = {}
4358
 
        for key, value in option_dict.items():
4359
 
            if isinstance(key, text_type):
4360
 
                key = key.encode('utf8')
4361
 
            if isinstance(value, text_type):
4362
 
                value = value.encode('utf8')
4363
 
            utf8_dict[key] = value
4364
 
        return bencode.bencode(utf8_dict)
4365
 
 
4366
 
    def _set_config_option_dict(self, value, name, section):
4367
 
        try:
4368
 
            path = self._branch._remote_path()
4369
 
            serialised_dict = self._serialize_option_dict(value)
4370
 
            response = self._branch._client.call(
4371
 
                b'Branch.set_config_option_dict',
4372
 
                path, self._branch._lock_token, self._branch._repo_lock_token,
4373
 
                serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4374
 
        except errors.UnknownSmartMethod:
4375
 
            medium = self._branch._client._medium
4376
 
            medium._remember_remote_is_before((2, 2))
4377
 
            return self._vfs_set_option(value, name, section)
4378
 
        if response != ():
4379
 
            raise errors.UnexpectedSmartServerResponse(response)
4380
 
 
4381
2788
    def _real_object(self):
4382
2789
        self._branch._ensure_real()
4383
2790
        return self._branch._real_branch
4395
2802
 
4396
2803
    def _get_configobj(self):
4397
2804
        medium = self._bzrdir._client._medium
4398
 
        verb = b'BzrDir.get_config_file'
 
2805
        verb = 'BzrDir.get_config_file'
4399
2806
        if medium._is_remote_before((1, 15)):
4400
2807
            raise errors.UnknownSmartMethod(verb)
4401
2808
        path = self._bzrdir._path_for_remote_call(self._bzrdir._client)
4422
2829
        return self._bzrdir._real_bzrdir
4423
2830
 
4424
2831
 
4425
 
error_translators = registry.Registry()
4426
 
no_context_error_translators = registry.Registry()
 
2832
 
 
2833
def _extract_tar(tar, to_dir):
 
2834
    """Extract all the contents of a tarfile object.
 
2835
 
 
2836
    A replacement for extractall, which is not present in python2.4
 
2837
    """
 
2838
    for tarinfo in tar:
 
2839
        tar.extract(tarinfo, to_dir)
4427
2840
 
4428
2841
 
4429
2842
def _translate_error(err, **context):
4443
2856
    def find(name):
4444
2857
        try:
4445
2858
            return context[name]
4446
 
        except KeyError:
4447
 
            mutter('Missing key \'%s\' in context %r', name, context)
 
2859
        except KeyError, key_err:
 
2860
            mutter('Missing key %r in context %r', key_err.args[0], context)
4448
2861
            raise err
4449
 
 
4450
2862
    def get_path():
4451
2863
        """Get the path from the context if present, otherwise use first error
4452
2864
        arg.
4453
2865
        """
4454
2866
        try:
4455
2867
            return context['path']
4456
 
        except KeyError:
 
2868
        except KeyError, key_err:
4457
2869
            try:
4458
 
                return err.error_args[0].decode('utf-8')
4459
 
            except IndexError:
4460
 
                mutter('Missing key \'path\' in context %r', context)
 
2870
                return err.error_args[0]
 
2871
            except IndexError, idx_err:
 
2872
                mutter(
 
2873
                    'Missing key %r in context %r', key_err.args[0], context)
4461
2874
                raise err
4462
 
    if not isinstance(err.error_verb, bytes):
4463
 
        raise TypeError(err.error_verb)
4464
 
    try:
4465
 
        translator = error_translators.get(err.error_verb)
4466
 
    except KeyError:
4467
 
        pass
4468
 
    else:
4469
 
        raise translator(err, find, get_path)
4470
 
    try:
4471
 
        translator = no_context_error_translators.get(err.error_verb)
4472
 
    except KeyError:
4473
 
        raise errors.UnknownErrorFromSmartServer(err)
4474
 
    else:
4475
 
        raise translator(err)
4476
 
 
4477
 
 
4478
 
error_translators.register(b'NoSuchRevision',
4479
 
                           lambda err, find, get_path: NoSuchRevision(
4480
 
                               find('branch'), err.error_args[0]))
4481
 
error_translators.register(b'nosuchrevision',
4482
 
                           lambda err, find, get_path: NoSuchRevision(
4483
 
                               find('repository'), err.error_args[0]))
4484
 
error_translators.register(
4485
 
    b'revno-outofbounds',
4486
 
    lambda err, find, get_path: errors.RevnoOutOfBounds(
4487
 
        err.error_args[0], (err.error_args[1], err.error_args[2])))
4488
 
 
4489
 
 
4490
 
def _translate_nobranch_error(err, find, get_path):
4491
 
    if len(err.error_args) >= 1:
4492
 
        extra = err.error_args[0].decode('utf-8')
4493
 
    else:
4494
 
        extra = None
4495
 
    return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4496
 
                                 detail=extra)
4497
 
 
4498
 
 
4499
 
error_translators.register(b'nobranch', _translate_nobranch_error)
4500
 
error_translators.register(b'norepository',
4501
 
                           lambda err, find, get_path: errors.NoRepositoryPresent(
4502
 
                               find('bzrdir')))
4503
 
error_translators.register(b'UnlockableTransport',
4504
 
                           lambda err, find, get_path: errors.UnlockableTransport(
4505
 
                               find('bzrdir').root_transport))
4506
 
error_translators.register(b'TokenMismatch',
4507
 
                           lambda err, find, get_path: errors.TokenMismatch(
4508
 
                               find('token'), '(remote token)'))
4509
 
error_translators.register(b'Diverged',
4510
 
                           lambda err, find, get_path: errors.DivergedBranches(
4511
 
                               find('branch'), find('other_branch')))
4512
 
error_translators.register(b'NotStacked',
4513
 
                           lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4514
 
 
4515
 
 
4516
 
def _translate_PermissionDenied(err, find, get_path):
4517
 
    path = get_path()
4518
 
    if len(err.error_args) >= 2:
4519
 
        extra = err.error_args[1].decode('utf-8')
4520
 
    else:
4521
 
        extra = None
4522
 
    return errors.PermissionDenied(path, extra=extra)
4523
 
 
4524
 
 
4525
 
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4526
 
error_translators.register(b'ReadError',
4527
 
                           lambda err, find, get_path: errors.ReadError(get_path()))
4528
 
error_translators.register(b'NoSuchFile',
4529
 
                           lambda err, find, get_path: errors.NoSuchFile(get_path()))
4530
 
error_translators.register(b'TokenLockingNotSupported',
4531
 
                           lambda err, find, get_path: errors.TokenLockingNotSupported(
4532
 
                               find('repository')))
4533
 
error_translators.register(b'UnsuspendableWriteGroup',
4534
 
                           lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4535
 
                               repository=find('repository')))
4536
 
error_translators.register(b'UnresumableWriteGroup',
4537
 
                           lambda err, find, get_path: errors.UnresumableWriteGroup(
4538
 
                               repository=find('repository'), write_groups=err.error_args[0],
4539
 
                               reason=err.error_args[1]))
4540
 
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4541
 
                                      lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4542
 
no_context_error_translators.register(b'IncompatibleRepositories',
4543
 
                                      lambda err: errors.IncompatibleRepositories(
4544
 
                                          err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4545
 
no_context_error_translators.register(b'LockContention',
4546
 
                                      lambda err: errors.LockContention('(remote lock)'))
4547
 
no_context_error_translators.register(b'LockFailed',
4548
 
                                      lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4549
 
no_context_error_translators.register(b'TipChangeRejected',
4550
 
                                      lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4551
 
no_context_error_translators.register(b'UnstackableBranchFormat',
4552
 
                                      lambda err: branch.UnstackableBranchFormat(*err.error_args))
4553
 
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4554
 
                                      lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4555
 
no_context_error_translators.register(b'FileExists',
4556
 
                                      lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4557
 
no_context_error_translators.register(b'DirectoryNotEmpty',
4558
 
                                      lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4559
 
no_context_error_translators.register(b'UnknownFormat',
4560
 
                                      lambda err: errors.UnknownFormatError(
4561
 
                                          err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4562
 
no_context_error_translators.register(b'InvalidURL',
4563
 
                                      lambda err: urlutils.InvalidURL(
4564
 
                                          err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4565
 
 
4566
 
 
4567
 
def _translate_short_readv_error(err):
4568
 
    args = err.error_args
4569
 
    return errors.ShortReadvError(
4570
 
        args[0].decode('utf-8'),
4571
 
        int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4572
 
        int(args[3].decode('ascii')))
4573
 
 
4574
 
 
4575
 
no_context_error_translators.register(b'ShortReadvError',
4576
 
                                      _translate_short_readv_error)
4577
 
 
4578
 
 
4579
 
def _translate_unicode_error(err):
4580
 
    encoding = err.error_args[0].decode('ascii')
4581
 
    val = err.error_args[1].decode('utf-8')
4582
 
    start = int(err.error_args[2].decode('ascii'))
4583
 
    end = int(err.error_args[3].decode('ascii'))
4584
 
    reason = err.error_args[4].decode('utf-8')
4585
 
    if val.startswith('u:'):
4586
 
        val = val[2:].decode('utf-8')
4587
 
    elif val.startswith('s:'):
4588
 
        val = val[2:].decode('base64')
4589
 
    if err.error_verb == 'UnicodeDecodeError':
4590
 
        raise UnicodeDecodeError(encoding, val, start, end, reason)
4591
 
    elif err.error_verb == 'UnicodeEncodeError':
4592
 
        raise UnicodeEncodeError(encoding, val, start, end, reason)
4593
 
 
4594
 
 
4595
 
no_context_error_translators.register(b'UnicodeEncodeError',
4596
 
                                      _translate_unicode_error)
4597
 
no_context_error_translators.register(b'UnicodeDecodeError',
4598
 
                                      _translate_unicode_error)
4599
 
no_context_error_translators.register(b'ReadOnlyError',
4600
 
                                      lambda err: errors.TransportNotPossible('readonly transport'))
4601
 
no_context_error_translators.register(b'MemoryError',
4602
 
                                      lambda err: errors.BzrError("remote server out of memory\n"
4603
 
                                                                  "Retry non-remotely, or contact the server admin for details."))
4604
 
no_context_error_translators.register(b'RevisionNotPresent',
4605
 
                                      lambda err: errors.RevisionNotPresent(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4606
 
 
4607
 
no_context_error_translators.register(b'BzrCheckError',
4608
 
                                      lambda err: errors.BzrCheckError(msg=err.error_args[0].decode('utf-8')))
 
2875
 
 
2876
    if err.error_verb == 'IncompatibleRepositories':
 
2877
        raise errors.IncompatibleRepositories(err.error_args[0],
 
2878
            err.error_args[1], err.error_args[2])
 
2879
    elif err.error_verb == 'NoSuchRevision':
 
2880
        raise NoSuchRevision(find('branch'), err.error_args[0])
 
2881
    elif err.error_verb == 'nosuchrevision':
 
2882
        raise NoSuchRevision(find('repository'), err.error_args[0])
 
2883
    elif err.error_verb == 'nobranch':
 
2884
        if len(err.error_args) >= 1:
 
2885
            extra = err.error_args[0]
 
2886
        else:
 
2887
            extra = None
 
2888
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
2889
            detail=extra)
 
2890
    elif err.error_verb == 'norepository':
 
2891
        raise errors.NoRepositoryPresent(find('bzrdir'))
 
2892
    elif err.error_verb == 'LockContention':
 
2893
        raise errors.LockContention('(remote lock)')
 
2894
    elif err.error_verb == 'UnlockableTransport':
 
2895
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
 
2896
    elif err.error_verb == 'LockFailed':
 
2897
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
2898
    elif err.error_verb == 'TokenMismatch':
 
2899
        raise errors.TokenMismatch(find('token'), '(remote token)')
 
2900
    elif err.error_verb == 'Diverged':
 
2901
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
 
2902
    elif err.error_verb == 'TipChangeRejected':
 
2903
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
 
2904
    elif err.error_verb == 'UnstackableBranchFormat':
 
2905
        raise errors.UnstackableBranchFormat(*err.error_args)
 
2906
    elif err.error_verb == 'UnstackableRepositoryFormat':
 
2907
        raise errors.UnstackableRepositoryFormat(*err.error_args)
 
2908
    elif err.error_verb == 'NotStacked':
 
2909
        raise errors.NotStacked(branch=find('branch'))
 
2910
    elif err.error_verb == 'PermissionDenied':
 
2911
        path = get_path()
 
2912
        if len(err.error_args) >= 2:
 
2913
            extra = err.error_args[1]
 
2914
        else:
 
2915
            extra = None
 
2916
        raise errors.PermissionDenied(path, extra=extra)
 
2917
    elif err.error_verb == 'ReadError':
 
2918
        path = get_path()
 
2919
        raise errors.ReadError(path)
 
2920
    elif err.error_verb == 'NoSuchFile':
 
2921
        path = get_path()
 
2922
        raise errors.NoSuchFile(path)
 
2923
    elif err.error_verb == 'FileExists':
 
2924
        raise errors.FileExists(err.error_args[0])
 
2925
    elif err.error_verb == 'DirectoryNotEmpty':
 
2926
        raise errors.DirectoryNotEmpty(err.error_args[0])
 
2927
    elif err.error_verb == 'ShortReadvError':
 
2928
        args = err.error_args
 
2929
        raise errors.ShortReadvError(
 
2930
            args[0], int(args[1]), int(args[2]), int(args[3]))
 
2931
    elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
 
2932
        encoding = str(err.error_args[0]) # encoding must always be a string
 
2933
        val = err.error_args[1]
 
2934
        start = int(err.error_args[2])
 
2935
        end = int(err.error_args[3])
 
2936
        reason = str(err.error_args[4]) # reason must always be a string
 
2937
        if val.startswith('u:'):
 
2938
            val = val[2:].decode('utf-8')
 
2939
        elif val.startswith('s:'):
 
2940
            val = val[2:].decode('base64')
 
2941
        if err.error_verb == 'UnicodeDecodeError':
 
2942
            raise UnicodeDecodeError(encoding, val, start, end, reason)
 
2943
        elif err.error_verb == 'UnicodeEncodeError':
 
2944
            raise UnicodeEncodeError(encoding, val, start, end, reason)
 
2945
    elif err.error_verb == 'ReadOnlyError':
 
2946
        raise errors.TransportNotPossible('readonly transport')
 
2947
    raise errors.UnknownErrorFromSmartServer(err)