/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/bzr/remote.py

  • Committer: Martin
  • Date: 2017-08-26 15:28:39 UTC
  • mto: This revision was merged to the branch mainline in revision 6762.
  • Revision ID: gzlist@googlemail.com-20170826152839-ka2fimri0mskfkct
Fix test_lazy_re on Python 3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2012 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
 
17
19
import bz2
 
20
import zlib
18
21
 
19
 
from bzrlib import (
 
22
from .. import (
20
23
    bencode,
21
24
    branch,
22
 
    bzrdir,
23
 
    config,
 
25
    bzr as _mod_bzr,
 
26
    config as _mod_config,
 
27
    controldir,
24
28
    debug,
25
29
    errors,
 
30
    gpg,
26
31
    graph,
27
32
    lock,
28
33
    lockdir,
29
 
    repository,
 
34
    osutils,
 
35
    registry,
30
36
    repository as _mod_repository,
31
 
    revision,
32
37
    revision as _mod_revision,
33
38
    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 (
 
39
    testament as _mod_testament,
 
40
    urlutils,
 
41
    )
 
42
from . import (
 
43
    branch as bzrbranch,
 
44
    bzrdir as _mod_bzrdir,
 
45
    inventory_delta,
 
46
    vf_repository,
 
47
    vf_search,
 
48
    )
 
49
from .branch import BranchReferenceFormat
 
50
from ..branch import BranchWriteLockResult
 
51
from ..decorators import needs_read_lock, needs_write_lock, only_raises
 
52
from ..errors import (
40
53
    NoSuchRevision,
41
54
    SmartProtocolError,
42
55
    )
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
 
56
from ..i18n import gettext
 
57
from .inventory import Inventory
 
58
from .inventorytree import InventoryRevisionTree
 
59
from ..lockable_files import LockableFiles
 
60
from ..sixish import (
 
61
    viewitems,
 
62
    viewvalues,
 
63
    )
 
64
from .smart import client, vfs, repository as smart_repo
 
65
from .smart.client import _SmartClient
 
66
from ..revision import NULL_REVISION
 
67
from ..repository import RepositoryWriteLockResult, _LazyListJoin
 
68
from .serializer import format_registry as serializer_format_registry
 
69
from ..trace import mutter, note, warning, log_exception_quietly
 
70
from .versionedfile import FulltextContentFactory
 
71
 
 
72
 
 
73
_DEFAULT_SEARCH_DEPTH = 100
47
74
 
48
75
 
49
76
class _RpcHelper(object):
52
79
    def _call(self, method, *args, **err_context):
53
80
        try:
54
81
            return self._client.call(method, *args)
55
 
        except errors.ErrorFromSmartServer, err:
 
82
        except errors.ErrorFromSmartServer as err:
56
83
            self._translate_error(err, **err_context)
57
84
 
58
85
    def _call_expecting_body(self, method, *args, **err_context):
59
86
        try:
60
87
            return self._client.call_expecting_body(method, *args)
61
 
        except errors.ErrorFromSmartServer, err:
 
88
        except errors.ErrorFromSmartServer as err:
62
89
            self._translate_error(err, **err_context)
63
90
 
64
91
    def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
65
92
        try:
66
93
            return self._client.call_with_body_bytes(method, args, body_bytes)
67
 
        except errors.ErrorFromSmartServer, err:
 
94
        except errors.ErrorFromSmartServer as err:
68
95
            self._translate_error(err, **err_context)
69
96
 
70
97
    def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
72
99
        try:
73
100
            return self._client.call_with_body_bytes_expecting_body(
74
101
                method, args, body_bytes)
75
 
        except errors.ErrorFromSmartServer, err:
 
102
        except errors.ErrorFromSmartServer as err:
76
103
            self._translate_error(err, **err_context)
77
104
 
78
105
 
86
113
    return format
87
114
 
88
115
 
89
 
# Note: RemoteBzrDirFormat is in bzrdir.py
90
 
 
91
 
class RemoteBzrDir(BzrDir, _RpcHelper):
 
116
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
 
117
# does not have to be imported unless a remote format is involved.
 
118
 
 
119
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
 
120
    """Format representing bzrdirs accessed via a smart server"""
 
121
 
 
122
    supports_workingtrees = False
 
123
 
 
124
    colocated_branches = False
 
125
 
 
126
    def __init__(self):
 
127
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
 
128
        # XXX: It's a bit ugly that the network name is here, because we'd
 
129
        # like to believe that format objects are stateless or at least
 
130
        # immutable,  However, we do at least avoid mutating the name after
 
131
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
 
132
        self._network_name = None
 
133
 
 
134
    def __repr__(self):
 
135
        return "%s(_network_name=%r)" % (self.__class__.__name__,
 
136
            self._network_name)
 
137
 
 
138
    def get_format_description(self):
 
139
        if self._network_name:
 
140
            try:
 
141
                real_format = controldir.network_format_registry.get(
 
142
                        self._network_name)
 
143
            except KeyError:
 
144
                pass
 
145
            else:
 
146
                return 'Remote: ' + real_format.get_format_description()
 
147
        return 'bzr remote bzrdir'
 
148
 
 
149
    def get_format_string(self):
 
150
        raise NotImplementedError(self.get_format_string)
 
151
 
 
152
    def network_name(self):
 
153
        if self._network_name:
 
154
            return self._network_name
 
155
        else:
 
156
            raise AssertionError("No network name set.")
 
157
 
 
158
    def initialize_on_transport(self, transport):
 
159
        try:
 
160
            # hand off the request to the smart server
 
161
            client_medium = transport.get_smart_medium()
 
162
        except errors.NoSmartMedium:
 
163
            # TODO: lookup the local format from a server hint.
 
164
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
165
            return local_dir_format.initialize_on_transport(transport)
 
166
        client = _SmartClient(client_medium)
 
167
        path = client.remote_path_from_transport(transport)
 
168
        try:
 
169
            response = client.call('BzrDirFormat.initialize', path)
 
170
        except errors.ErrorFromSmartServer as err:
 
171
            _translate_error(err, path=path)
 
172
        if response[0] != 'ok':
 
173
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
 
174
        format = RemoteBzrDirFormat()
 
175
        self._supply_sub_formats_to(format)
 
176
        return RemoteBzrDir(transport, format)
 
177
 
 
178
    def parse_NoneTrueFalse(self, arg):
 
179
        if not arg:
 
180
            return None
 
181
        if arg == 'False':
 
182
            return False
 
183
        if arg == 'True':
 
184
            return True
 
185
        raise AssertionError("invalid arg %r" % arg)
 
186
 
 
187
    def _serialize_NoneTrueFalse(self, arg):
 
188
        if arg is False:
 
189
            return 'False'
 
190
        if arg:
 
191
            return 'True'
 
192
        return ''
 
193
 
 
194
    def _serialize_NoneString(self, arg):
 
195
        return arg or ''
 
196
 
 
197
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
198
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
199
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
200
        shared_repo=False):
 
201
        try:
 
202
            # hand off the request to the smart server
 
203
            client_medium = transport.get_smart_medium()
 
204
        except errors.NoSmartMedium:
 
205
            do_vfs = True
 
206
        else:
 
207
            # Decline to open it if the server doesn't support our required
 
208
            # version (3) so that the VFS-based transport will do it.
 
209
            if client_medium.should_probe():
 
210
                try:
 
211
                    server_version = client_medium.protocol_version()
 
212
                    if server_version != '2':
 
213
                        do_vfs = True
 
214
                    else:
 
215
                        do_vfs = False
 
216
                except errors.SmartProtocolError:
 
217
                    # Apparently there's no usable smart server there, even though
 
218
                    # the medium supports the smart protocol.
 
219
                    do_vfs = True
 
220
            else:
 
221
                do_vfs = False
 
222
        if not do_vfs:
 
223
            client = _SmartClient(client_medium)
 
224
            path = client.remote_path_from_transport(transport)
 
225
            if client_medium._is_remote_before((1, 16)):
 
226
                do_vfs = True
 
227
        if do_vfs:
 
228
            # TODO: lookup the local format from a server hint.
 
229
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
230
            self._supply_sub_formats_to(local_dir_format)
 
231
            return local_dir_format.initialize_on_transport_ex(transport,
 
232
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
233
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
234
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
235
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
236
                vfs_only=True)
 
237
        return self._initialize_on_transport_ex_rpc(client, path, transport,
 
238
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
239
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
 
240
 
 
241
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
 
242
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
243
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
 
244
        args = []
 
245
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
 
246
        args.append(self._serialize_NoneTrueFalse(create_prefix))
 
247
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
 
248
        args.append(self._serialize_NoneString(stacked_on))
 
249
        # stack_on_pwd is often/usually our transport
 
250
        if stack_on_pwd:
 
251
            try:
 
252
                stack_on_pwd = transport.relpath(stack_on_pwd)
 
253
                if not stack_on_pwd:
 
254
                    stack_on_pwd = '.'
 
255
            except errors.PathNotChild:
 
256
                pass
 
257
        args.append(self._serialize_NoneString(stack_on_pwd))
 
258
        args.append(self._serialize_NoneString(repo_format_name))
 
259
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
 
260
        args.append(self._serialize_NoneTrueFalse(shared_repo))
 
261
        request_network_name = self._network_name or \
 
262
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
 
263
        try:
 
264
            response = client.call('BzrDirFormat.initialize_ex_1.16',
 
265
                request_network_name, path, *args)
 
266
        except errors.UnknownSmartMethod:
 
267
            client._medium._remember_remote_is_before((1,16))
 
268
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
269
            self._supply_sub_formats_to(local_dir_format)
 
270
            return local_dir_format.initialize_on_transport_ex(transport,
 
271
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
272
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
273
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
274
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
275
                vfs_only=True)
 
276
        except errors.ErrorFromSmartServer as err:
 
277
            _translate_error(err, path=path)
 
278
        repo_path = response[0]
 
279
        bzrdir_name = response[6]
 
280
        require_stacking = response[7]
 
281
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
 
282
        format = RemoteBzrDirFormat()
 
283
        format._network_name = bzrdir_name
 
284
        self._supply_sub_formats_to(format)
 
285
        bzrdir = RemoteBzrDir(transport, format, _client=client)
 
286
        if repo_path:
 
287
            repo_format = response_tuple_to_repo_format(response[1:])
 
288
            if repo_path == '.':
 
289
                repo_path = ''
 
290
            if repo_path:
 
291
                repo_bzrdir_format = RemoteBzrDirFormat()
 
292
                repo_bzrdir_format._network_name = response[5]
 
293
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
 
294
                    repo_bzrdir_format)
 
295
            else:
 
296
                repo_bzr = bzrdir
 
297
            final_stack = response[8] or None
 
298
            final_stack_pwd = response[9] or None
 
299
            if final_stack_pwd:
 
300
                final_stack_pwd = urlutils.join(
 
301
                    transport.base, final_stack_pwd)
 
302
            remote_repo = RemoteRepository(repo_bzr, repo_format)
 
303
            if len(response) > 10:
 
304
                # Updated server verb that locks remotely.
 
305
                repo_lock_token = response[10] or None
 
306
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
 
307
                if repo_lock_token:
 
308
                    remote_repo.dont_leave_lock_in_place()
 
309
            else:
 
310
                remote_repo.lock_write()
 
311
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
 
312
                final_stack_pwd, require_stacking)
 
313
            policy.acquire_repository()
 
314
        else:
 
315
            remote_repo = None
 
316
            policy = None
 
317
        bzrdir._format.set_branch_format(self.get_branch_format())
 
318
        if require_stacking:
 
319
            # The repo has already been created, but we need to make sure that
 
320
            # we'll make a stackable branch.
 
321
            bzrdir._format.require_stacking(_skip_repo=True)
 
322
        return remote_repo, bzrdir, require_stacking, policy
 
323
 
 
324
    def _open(self, transport):
 
325
        return RemoteBzrDir(transport, self)
 
326
 
 
327
    def __eq__(self, other):
 
328
        if not isinstance(other, RemoteBzrDirFormat):
 
329
            return False
 
330
        return self.get_format_description() == other.get_format_description()
 
331
 
 
332
    def __return_repository_format(self):
 
333
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
 
334
        # repository format has been asked for, tell the RemoteRepositoryFormat
 
335
        # that it should use that for init() etc.
 
336
        result = RemoteRepositoryFormat()
 
337
        custom_format = getattr(self, '_repository_format', None)
 
338
        if custom_format:
 
339
            if isinstance(custom_format, RemoteRepositoryFormat):
 
340
                return custom_format
 
341
            else:
 
342
                # We will use the custom format to create repositories over the
 
343
                # wire; expose its details like rich_root_data for code to
 
344
                # query
 
345
                result._custom_format = custom_format
 
346
        return result
 
347
 
 
348
    def get_branch_format(self):
 
349
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
 
350
        if not isinstance(result, RemoteBranchFormat):
 
351
            new_result = RemoteBranchFormat()
 
352
            new_result._custom_format = result
 
353
            # cache the result
 
354
            self.set_branch_format(new_result)
 
355
            result = new_result
 
356
        return result
 
357
 
 
358
    repository_format = property(__return_repository_format,
 
359
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
 
360
 
 
361
 
 
362
class RemoteControlStore(_mod_config.IniFileStore):
 
363
    """Control store which attempts to use HPSS calls to retrieve control store.
 
364
 
 
365
    Note that this is specific to bzr-based formats.
 
366
    """
 
367
 
 
368
    def __init__(self, bzrdir):
 
369
        super(RemoteControlStore, self).__init__()
 
370
        self.controldir = bzrdir
 
371
        self._real_store = None
 
372
 
 
373
    def lock_write(self, token=None):
 
374
        self._ensure_real()
 
375
        return self._real_store.lock_write(token)
 
376
 
 
377
    def unlock(self):
 
378
        self._ensure_real()
 
379
        return self._real_store.unlock()
 
380
 
 
381
    @needs_write_lock
 
382
    def save(self):
 
383
        # We need to be able to override the undecorated implementation
 
384
        self.save_without_locking()
 
385
 
 
386
    def save_without_locking(self):
 
387
        super(RemoteControlStore, self).save()
 
388
 
 
389
    def _ensure_real(self):
 
390
        self.controldir._ensure_real()
 
391
        if self._real_store is None:
 
392
            self._real_store = _mod_config.ControlStore(self.controldir)
 
393
 
 
394
    def external_url(self):
 
395
        return urlutils.join(self.branch.user_url, 'control.conf')
 
396
 
 
397
    def _load_content(self):
 
398
        medium = self.controldir._client._medium
 
399
        path = self.controldir._path_for_remote_call(self.controldir._client)
 
400
        try:
 
401
            response, handler = self.controldir._call_expecting_body(
 
402
                'BzrDir.get_config_file', path)
 
403
        except errors.UnknownSmartMethod:
 
404
            self._ensure_real()
 
405
            return self._real_store._load_content()
 
406
        if len(response) and response[0] != 'ok':
 
407
            raise errors.UnexpectedSmartServerResponse(response)
 
408
        return handler.read_body_bytes()
 
409
 
 
410
    def _save_content(self, content):
 
411
        # FIXME JRV 2011-11-22: Ideally this should use a
 
412
        # HPSS call too, but at the moment it is not possible
 
413
        # to write lock control directories.
 
414
        self._ensure_real()
 
415
        return self._real_store._save_content(content)
 
416
 
 
417
 
 
418
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
419
    """Control directory on a remote server, accessed via bzr:// or similar."""
93
420
 
94
421
    def __init__(self, transport, format, _client=None, _force_probe=False):
97
424
        :param _client: Private parameter for testing. Disables probing and the
98
425
            use of a real bzrdir.
99
426
        """
100
 
        BzrDir.__init__(self, transport, format)
 
427
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
101
428
        # this object holds a delegated bzrdir that uses file-level operations
102
429
        # to talk to the other side
103
430
        self._real_bzrdir = None
163
490
                import traceback
164
491
                warning('VFS BzrDir access triggered\n%s',
165
492
                    ''.join(traceback.format_stack()))
166
 
            self._real_bzrdir = BzrDir.open_from_transport(
167
 
                self.root_transport, _server_formats=False)
 
493
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
 
494
                self.root_transport, probers=[_mod_bzr.BzrProber])
168
495
            self._format._network_name = \
169
496
                self._real_bzrdir._format.network_name()
170
497
 
175
502
        # Prevent aliasing problems in the next_open_branch_result cache.
176
503
        # See create_branch for rationale.
177
504
        self._next_open_branch_result = None
178
 
        return BzrDir.break_lock(self)
 
505
        return _mod_bzrdir.BzrDir.break_lock(self)
 
506
 
 
507
    def _vfs_checkout_metadir(self):
 
508
        self._ensure_real()
 
509
        return self._real_bzrdir.checkout_metadir()
 
510
 
 
511
    def checkout_metadir(self):
 
512
        """Retrieve the controldir format to use for checkouts of this one.
 
513
        """
 
514
        medium = self._client._medium
 
515
        if medium._is_remote_before((2, 5)):
 
516
            return self._vfs_checkout_metadir()
 
517
        path = self._path_for_remote_call(self._client)
 
518
        try:
 
519
            response = self._client.call('BzrDir.checkout_metadir',
 
520
                path)
 
521
        except errors.UnknownSmartMethod:
 
522
            medium._remember_remote_is_before((2, 5))
 
523
            return self._vfs_checkout_metadir()
 
524
        if len(response) != 3:
 
525
            raise errors.UnexpectedSmartServerResponse(response)
 
526
        control_name, repo_name, branch_name = response
 
527
        try:
 
528
            format = controldir.network_format_registry.get(control_name)
 
529
        except KeyError:
 
530
            raise errors.UnknownFormatError(kind='control',
 
531
                format=control_name)
 
532
        if repo_name:
 
533
            try:
 
534
                repo_format = _mod_repository.network_format_registry.get(
 
535
                    repo_name)
 
536
            except KeyError:
 
537
                raise errors.UnknownFormatError(kind='repository',
 
538
                    format=repo_name)
 
539
            format.repository_format = repo_format
 
540
        if branch_name:
 
541
            try:
 
542
                format.set_branch_format(
 
543
                    branch.network_format_registry.get(branch_name))
 
544
            except KeyError:
 
545
                raise errors.UnknownFormatError(kind='branch',
 
546
                    format=branch_name)
 
547
        return format
179
548
 
180
549
    def _vfs_cloning_metadir(self, require_stacking=False):
181
550
        self._ensure_real()
197
566
        except errors.UnknownSmartMethod:
198
567
            medium._remember_remote_is_before((1, 13))
199
568
            return self._vfs_cloning_metadir(require_stacking=require_stacking)
200
 
        except errors.UnknownErrorFromSmartServer, err:
 
569
        except errors.UnknownErrorFromSmartServer as err:
201
570
            if err.error_tuple != ('BranchReference',):
202
571
                raise
203
572
            # We need to resolve the branch reference to determine the
205
574
            # referenced branch (and bzrdir, etc) but only when the caller
206
575
            # didn't already resolve the branch reference.
207
576
            referenced_branch = self.open_branch()
208
 
            return referenced_branch.bzrdir.cloning_metadir()
 
577
            return referenced_branch.controldir.cloning_metadir()
209
578
        if len(response) != 3:
210
579
            raise errors.UnexpectedSmartServerResponse(response)
211
580
        control_name, repo_name, branch_info = response
212
581
        if len(branch_info) != 2:
213
582
            raise errors.UnexpectedSmartServerResponse(response)
214
583
        branch_ref, branch_name = branch_info
215
 
        format = bzrdir.network_format_registry.get(control_name)
 
584
        try:
 
585
            format = controldir.network_format_registry.get(control_name)
 
586
        except KeyError:
 
587
            raise errors.UnknownFormatError(kind='control', format=control_name)
 
588
 
216
589
        if repo_name:
217
 
            format.repository_format = repository.network_format_registry.get(
218
 
                repo_name)
 
590
            try:
 
591
                format.repository_format = _mod_repository.network_format_registry.get(
 
592
                    repo_name)
 
593
            except KeyError:
 
594
                raise errors.UnknownFormatError(kind='repository',
 
595
                    format=repo_name)
219
596
        if branch_ref == 'ref':
220
597
            # XXX: we need possible_transports here to avoid reopening the
221
598
            # connection to the referenced location
222
 
            ref_bzrdir = BzrDir.open(branch_name)
 
599
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
600
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
601
            format.set_branch_format(branch_format)
225
602
        elif branch_ref == 'branch':
226
603
            if branch_name:
227
 
                format.set_branch_format(
228
 
                    branch.network_format_registry.get(branch_name))
 
604
                try:
 
605
                    branch_format = branch.network_format_registry.get(
 
606
                        branch_name)
 
607
                except KeyError:
 
608
                    raise errors.UnknownFormatError(kind='branch',
 
609
                        format=branch_name)
 
610
                format.set_branch_format(branch_format)
229
611
        else:
230
612
            raise errors.UnexpectedSmartServerResponse(response)
231
613
        return format
241
623
 
242
624
    def destroy_repository(self):
243
625
        """See BzrDir.destroy_repository"""
244
 
        self._ensure_real()
245
 
        self._real_bzrdir.destroy_repository()
 
626
        path = self._path_for_remote_call(self._client)
 
627
        try:
 
628
            response = self._call('BzrDir.destroy_repository', path)
 
629
        except errors.UnknownSmartMethod:
 
630
            self._ensure_real()
 
631
            self._real_bzrdir.destroy_repository()
 
632
            return
 
633
        if response[0] != 'ok':
 
634
            raise SmartProtocolError('unexpected response code %s' % (response,))
246
635
 
247
 
    def create_branch(self, name=None):
 
636
    def create_branch(self, name=None, repository=None,
 
637
                      append_revisions_only=None):
 
638
        if name is None:
 
639
            name = self._get_selected_branch()
 
640
        if name != "":
 
641
            raise errors.NoColocatedBranchSupport(self)
248
642
        # as per meta1 formats - just delegate to the format object which may
249
643
        # be parameterised.
250
644
        real_branch = self._format.get_branch_format().initialize(self,
251
 
            name=name)
 
645
            name=name, repository=repository,
 
646
            append_revisions_only=append_revisions_only)
252
647
        if not isinstance(real_branch, RemoteBranch):
253
 
            result = RemoteBranch(self, self.find_repository(), real_branch,
254
 
                                  name=name)
 
648
            if not isinstance(repository, RemoteRepository):
 
649
                raise AssertionError(
 
650
                    'need a RemoteRepository to use with RemoteBranch, got %r'
 
651
                    % (repository,))
 
652
            result = RemoteBranch(self, repository, real_branch, name=name)
255
653
        else:
256
654
            result = real_branch
257
655
        # BzrDir.clone_on_transport() uses the result of create_branch but does
265
663
 
266
664
    def destroy_branch(self, name=None):
267
665
        """See BzrDir.destroy_branch"""
268
 
        self._ensure_real()
269
 
        self._real_bzrdir.destroy_branch(name=name)
 
666
        if name is None:
 
667
            name = self._get_selected_branch()
 
668
        if name != "":
 
669
            raise errors.NoColocatedBranchSupport(self)
 
670
        path = self._path_for_remote_call(self._client)
 
671
        try:
 
672
            if name != "":
 
673
                args = (name, )
 
674
            else:
 
675
                args = ()
 
676
            response = self._call('BzrDir.destroy_branch', path, *args)
 
677
        except errors.UnknownSmartMethod:
 
678
            self._ensure_real()
 
679
            self._real_bzrdir.destroy_branch(name=name)
 
680
            self._next_open_branch_result = None
 
681
            return
270
682
        self._next_open_branch_result = None
 
683
        if response[0] != 'ok':
 
684
            raise SmartProtocolError('unexpected response code %s' % (response,))
271
685
 
272
 
    def create_workingtree(self, revision_id=None, from_branch=None):
 
686
    def create_workingtree(self, revision_id=None, from_branch=None,
 
687
        accelerator_tree=None, hardlink=False):
273
688
        raise errors.NotLocalUrl(self.transport.base)
274
689
 
275
 
    def find_branch_format(self):
 
690
    def find_branch_format(self, name=None):
276
691
        """Find the branch 'format' for this bzrdir.
277
692
 
278
693
        This might be a synthetic object for e.g. RemoteBranch and SVN.
279
694
        """
280
 
        b = self.open_branch()
 
695
        b = self.open_branch(name=name)
281
696
        return b._format
282
697
 
283
 
    def get_branch_reference(self):
 
698
    def get_branches(self, possible_transports=None, ignore_fallbacks=False):
 
699
        path = self._path_for_remote_call(self._client)
 
700
        try:
 
701
            response, handler = self._call_expecting_body(
 
702
                'BzrDir.get_branches', path)
 
703
        except errors.UnknownSmartMethod:
 
704
            self._ensure_real()
 
705
            return self._real_bzrdir.get_branches()
 
706
        if response[0] != "success":
 
707
            raise errors.UnexpectedSmartServerResponse(response)
 
708
        body = bencode.bdecode(handler.read_body_bytes())
 
709
        ret = {}
 
710
        for name, value in viewitems(body):
 
711
            ret[name] = self._open_branch(name, value[0], value[1],
 
712
                possible_transports=possible_transports,
 
713
                ignore_fallbacks=ignore_fallbacks)
 
714
        return ret
 
715
 
 
716
    def set_branch_reference(self, target_branch, name=None):
 
717
        """See BzrDir.set_branch_reference()."""
 
718
        if name is None:
 
719
            name = self._get_selected_branch()
 
720
        if name != "":
 
721
            raise errors.NoColocatedBranchSupport(self)
 
722
        self._ensure_real()
 
723
        return self._real_bzrdir.set_branch_reference(target_branch, name=name)
 
724
 
 
725
    def get_branch_reference(self, name=None):
284
726
        """See BzrDir.get_branch_reference()."""
 
727
        if name is None:
 
728
            name = self._get_selected_branch()
 
729
        if name != "":
 
730
            raise errors.NoColocatedBranchSupport(self)
285
731
        response = self._get_branch_reference()
286
732
        if response[0] == 'ref':
287
733
            return response[1]
318
764
            raise errors.UnexpectedSmartServerResponse(response)
319
765
        return response
320
766
 
321
 
    def _get_tree_branch(self):
 
767
    def _get_tree_branch(self, name=None):
322
768
        """See BzrDir._get_tree_branch()."""
323
 
        return None, self.open_branch()
 
769
        return None, self.open_branch(name=name)
324
770
 
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':
 
771
    def _open_branch(self, name, kind, location_or_format,
 
772
                     ignore_fallbacks=False, possible_transports=None):
 
773
        if kind == 'ref':
336
774
            # a branch reference, use the existing BranchReference logic.
337
775
            format = BranchReferenceFormat()
338
776
            return format.open(self, name=name, _found=True,
339
 
                location=response[1], ignore_fallbacks=ignore_fallbacks)
340
 
        branch_format_name = response[1]
 
777
                location=location_or_format, ignore_fallbacks=ignore_fallbacks,
 
778
                possible_transports=possible_transports)
 
779
        branch_format_name = location_or_format
341
780
        if not branch_format_name:
342
781
            branch_format_name = None
343
782
        format = RemoteBranchFormat(network_name=branch_format_name)
344
783
        return RemoteBranch(self, self.find_repository(), format=format,
345
 
            setup_stacking=not ignore_fallbacks, name=name)
 
784
            setup_stacking=not ignore_fallbacks, name=name,
 
785
            possible_transports=possible_transports)
 
786
 
 
787
    def open_branch(self, name=None, unsupported=False,
 
788
                    ignore_fallbacks=False, possible_transports=None):
 
789
        if name is None:
 
790
            name = self._get_selected_branch()
 
791
        if name != "":
 
792
            raise errors.NoColocatedBranchSupport(self)
 
793
        if unsupported:
 
794
            raise NotImplementedError('unsupported flag support not implemented yet.')
 
795
        if self._next_open_branch_result is not None:
 
796
            # See create_branch for details.
 
797
            result = self._next_open_branch_result
 
798
            self._next_open_branch_result = None
 
799
            return result
 
800
        response = self._get_branch_reference()
 
801
        return self._open_branch(name, response[0], response[1],
 
802
            possible_transports=possible_transports,
 
803
            ignore_fallbacks=ignore_fallbacks)
346
804
 
347
805
    def _open_repo_v1(self, path):
348
806
        verb = 'BzrDir.find_repository'
411
869
 
412
870
    def has_workingtree(self):
413
871
        if self._has_working_tree is None:
414
 
            self._ensure_real()
415
 
            self._has_working_tree = self._real_bzrdir.has_workingtree()
 
872
            path = self._path_for_remote_call(self._client)
 
873
            try:
 
874
                response = self._call('BzrDir.has_workingtree', path)
 
875
            except errors.UnknownSmartMethod:
 
876
                self._ensure_real()
 
877
                self._has_working_tree = self._real_bzrdir.has_workingtree()
 
878
            else:
 
879
                if response[0] not in ('yes', 'no'):
 
880
                    raise SmartProtocolError('unexpected response code %s' % (response,))
 
881
                self._has_working_tree = (response[0] == 'yes')
416
882
        return self._has_working_tree
417
883
 
418
884
    def open_workingtree(self, recommend_upgrade=True):
423
889
 
424
890
    def _path_for_remote_call(self, client):
425
891
        """Return the path to be used for this bzrdir in a remote call."""
426
 
        return client.remote_path_from_transport(self.root_transport)
 
892
        return urlutils.split_segment_parameters_raw(
 
893
            client.remote_path_from_transport(self.root_transport))[0]
427
894
 
428
895
    def get_branch_transport(self, branch_format, name=None):
429
896
        self._ensure_real()
441
908
        """Upgrading of remote bzrdirs is not supported yet."""
442
909
        return False
443
910
 
444
 
    def needs_format_conversion(self, format=None):
 
911
    def needs_format_conversion(self, format):
445
912
        """Upgrading of remote bzrdirs is not supported yet."""
446
 
        if format is None:
447
 
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
448
 
                % 'needs_format_conversion(format=None)')
449
913
        return False
450
914
 
451
 
    def clone(self, url, revision_id=None, force_new_repo=False,
452
 
              preserve_stacking=False):
453
 
        self._ensure_real()
454
 
        return self._real_bzrdir.clone(url, revision_id=revision_id,
455
 
            force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
456
 
 
457
915
    def _get_config(self):
458
916
        return RemoteBzrDirConfig(self)
459
917
 
460
 
 
461
 
class RemoteRepositoryFormat(repository.RepositoryFormat):
 
918
    def _get_config_store(self):
 
919
        return RemoteControlStore(self)
 
920
 
 
921
 
 
922
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
923
    """Format for repositories accessed over a _SmartClient.
463
924
 
464
925
    Instances of this repository are represented by RemoteRepository
478
939
        to obtain data like the network name.
479
940
    """
480
941
 
481
 
    _matchingbzrdir = RemoteBzrDirFormat()
 
942
    _matchingcontroldir = RemoteBzrDirFormat()
 
943
    supports_full_versioned_files = True
 
944
    supports_leaving_lock = True
482
945
 
483
946
    def __init__(self):
484
 
        repository.RepositoryFormat.__init__(self)
 
947
        _mod_repository.RepositoryFormat.__init__(self)
485
948
        self._custom_format = None
486
949
        self._network_name = None
487
950
        self._creating_bzrdir = None
 
951
        self._revision_graph_can_have_wrong_parents = None
488
952
        self._supports_chks = None
489
953
        self._supports_external_lookups = None
490
954
        self._supports_tree_reference = None
 
955
        self._supports_funky_characters = None
 
956
        self._supports_nesting_repositories = None
491
957
        self._rich_root_data = None
492
958
 
493
959
    def __repr__(self):
522
988
        return self._supports_external_lookups
523
989
 
524
990
    @property
 
991
    def supports_funky_characters(self):
 
992
        if self._supports_funky_characters is None:
 
993
            self._ensure_real()
 
994
            self._supports_funky_characters = \
 
995
                self._custom_format.supports_funky_characters
 
996
        return self._supports_funky_characters
 
997
 
 
998
    @property
 
999
    def supports_nesting_repositories(self):
 
1000
        if self._supports_nesting_repositories is None:
 
1001
            self._ensure_real()
 
1002
            self._supports_nesting_repositories = \
 
1003
                self._custom_format.supports_nesting_repositories
 
1004
        return self._supports_nesting_repositories
 
1005
 
 
1006
    @property
525
1007
    def supports_tree_reference(self):
526
1008
        if self._supports_tree_reference is None:
527
1009
            self._ensure_real()
529
1011
                self._custom_format.supports_tree_reference
530
1012
        return self._supports_tree_reference
531
1013
 
532
 
    def _vfs_initialize(self, a_bzrdir, shared):
 
1014
    @property
 
1015
    def revision_graph_can_have_wrong_parents(self):
 
1016
        if self._revision_graph_can_have_wrong_parents is None:
 
1017
            self._ensure_real()
 
1018
            self._revision_graph_can_have_wrong_parents = \
 
1019
                self._custom_format.revision_graph_can_have_wrong_parents
 
1020
        return self._revision_graph_can_have_wrong_parents
 
1021
 
 
1022
    def _vfs_initialize(self, a_controldir, shared):
533
1023
        """Helper for common code in initialize."""
534
1024
        if self._custom_format:
535
1025
            # Custom format requested
536
 
            result = self._custom_format.initialize(a_bzrdir, shared=shared)
 
1026
            result = self._custom_format.initialize(a_controldir, shared=shared)
537
1027
        elif self._creating_bzrdir is not None:
538
1028
            # Use the format that the repository we were created to back
539
1029
            # has.
540
1030
            prior_repo = self._creating_bzrdir.open_repository()
541
1031
            prior_repo._ensure_real()
542
1032
            result = prior_repo._real_repository._format.initialize(
543
 
                a_bzrdir, shared=shared)
 
1033
                a_controldir, shared=shared)
544
1034
        else:
545
1035
            # assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1036
            # support remote initialization.
547
1037
            # We delegate to a real object at this point (as RemoteBzrDir
548
1038
            # delegate to the repository format which would lead to infinite
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)
 
1039
            # recursion if we just called a_controldir.create_repository.
 
1040
            a_controldir._ensure_real()
 
1041
            result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1042
        if not isinstance(result, RemoteRepository):
553
 
            return self.open(a_bzrdir)
 
1043
            return self.open(a_controldir)
554
1044
        else:
555
1045
            return result
556
1046
 
557
 
    def initialize(self, a_bzrdir, shared=False):
 
1047
    def initialize(self, a_controldir, shared=False):
558
1048
        # Being asked to create on a non RemoteBzrDir:
559
 
        if not isinstance(a_bzrdir, RemoteBzrDir):
560
 
            return self._vfs_initialize(a_bzrdir, shared)
561
 
        medium = a_bzrdir._client._medium
 
1049
        if not isinstance(a_controldir, RemoteBzrDir):
 
1050
            return self._vfs_initialize(a_controldir, shared)
 
1051
        medium = a_controldir._client._medium
562
1052
        if medium._is_remote_before((1, 13)):
563
 
            return self._vfs_initialize(a_bzrdir, shared)
 
1053
            return self._vfs_initialize(a_controldir, shared)
564
1054
        # Creating on a remote bzr dir.
565
1055
        # 1) get the network name to use.
566
1056
        if self._custom_format:
568
1058
        elif self._network_name:
569
1059
            network_name = self._network_name
570
1060
        else:
571
 
            # Select the current bzrlib default and ask for that.
572
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
1061
            # Select the current breezy default and ask for that.
 
1062
            reference_bzrdir_format = controldir.format_registry.get('default')()
573
1063
            reference_format = reference_bzrdir_format.repository_format
574
1064
            network_name = reference_format.network_name()
575
1065
        # 2) try direct creation via RPC
576
 
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
1066
        path = a_controldir._path_for_remote_call(a_controldir._client)
577
1067
        verb = 'BzrDir.create_repository'
578
1068
        if shared:
579
1069
            shared_str = 'True'
580
1070
        else:
581
1071
            shared_str = 'False'
582
1072
        try:
583
 
            response = a_bzrdir._call(verb, path, network_name, shared_str)
 
1073
            response = a_controldir._call(verb, path, network_name, shared_str)
584
1074
        except errors.UnknownSmartMethod:
585
1075
            # Fallback - use vfs methods
586
1076
            medium._remember_remote_is_before((1, 13))
587
 
            return self._vfs_initialize(a_bzrdir, shared)
 
1077
            return self._vfs_initialize(a_controldir, shared)
588
1078
        else:
589
1079
            # Turn the response into a RemoteRepository object.
590
1080
            format = response_tuple_to_repo_format(response[1:])
591
1081
            # Used to support creating a real format instance when needed.
592
 
            format._creating_bzrdir = a_bzrdir
593
 
            remote_repo = RemoteRepository(a_bzrdir, format)
 
1082
            format._creating_bzrdir = a_controldir
 
1083
            remote_repo = RemoteRepository(a_controldir, format)
594
1084
            format._creating_repo = remote_repo
595
1085
            return remote_repo
596
1086
 
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()
 
1087
    def open(self, a_controldir):
 
1088
        if not isinstance(a_controldir, RemoteBzrDir):
 
1089
            raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
 
1090
        return a_controldir.open_repository()
601
1091
 
602
1092
    def _ensure_real(self):
603
1093
        if self._custom_format is None:
604
 
            self._custom_format = repository.network_format_registry.get(
605
 
                self._network_name)
 
1094
            try:
 
1095
                self._custom_format = _mod_repository.network_format_registry.get(
 
1096
                    self._network_name)
 
1097
            except KeyError:
 
1098
                raise errors.UnknownFormatError(kind='repository',
 
1099
                    format=self._network_name)
606
1100
 
607
1101
    @property
608
1102
    def _fetch_order(self):
643
1137
        return self._custom_format._serializer
644
1138
 
645
1139
 
646
 
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
647
 
    bzrdir.ControlComponent):
 
1140
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
 
1141
        lock._RelockDebugMixin):
648
1142
    """Repository accessed over rpc.
649
1143
 
650
1144
    For the moment most operations are performed using local transport-backed
666
1160
            self._real_repository = real_repository
667
1161
        else:
668
1162
            self._real_repository = None
669
 
        self.bzrdir = remote_bzrdir
 
1163
        self.controldir = remote_bzrdir
670
1164
        if _client is None:
671
1165
            self._client = remote_bzrdir._client
672
1166
        else:
674
1168
        self._format = format
675
1169
        self._lock_mode = None
676
1170
        self._lock_token = None
 
1171
        self._write_group_tokens = None
677
1172
        self._lock_count = 0
678
1173
        self._leave_lock = False
679
1174
        # Cache of revision parents; misses are cached during read locks, and
689
1184
        self._reconcile_does_inventory_gc = False
690
1185
        self._reconcile_fixes_text_parents = False
691
1186
        self._reconcile_backsup_inventory = False
692
 
        self.base = self.bzrdir.transport.base
 
1187
        self.base = self.controldir.transport.base
693
1188
        # Additional places to query for data.
694
1189
        self._fallback_repositories = []
695
1190
 
696
1191
    @property
697
1192
    def user_transport(self):
698
 
        return self.bzrdir.user_transport
 
1193
        return self.controldir.user_transport
699
1194
 
700
1195
    @property
701
1196
    def control_transport(self):
702
1197
        # XXX: Normally you shouldn't directly get at the remote repository
703
1198
        # transport, but I'm not sure it's worth making this method
704
1199
        # optional -- mbp 2010-04-21
705
 
        return self.bzrdir.get_repository_transport(None)
706
 
        
 
1200
        return self.controldir.get_repository_transport(None)
 
1201
 
707
1202
    def __str__(self):
708
1203
        return "%s(%s)" % (self.__class__.__name__, self.base)
709
1204
 
719
1214
 
720
1215
        :param suppress_errors: see Repository.abort_write_group.
721
1216
        """
722
 
        self._ensure_real()
723
 
        return self._real_repository.abort_write_group(
724
 
            suppress_errors=suppress_errors)
 
1217
        if self._real_repository:
 
1218
            self._ensure_real()
 
1219
            return self._real_repository.abort_write_group(
 
1220
                suppress_errors=suppress_errors)
 
1221
        if not self.is_in_write_group():
 
1222
            if suppress_errors:
 
1223
                mutter('(suppressed) not in write group')
 
1224
                return
 
1225
            raise errors.BzrError("not in write group")
 
1226
        path = self.controldir._path_for_remote_call(self._client)
 
1227
        try:
 
1228
            response = self._call('Repository.abort_write_group', path,
 
1229
                self._lock_token, self._write_group_tokens)
 
1230
        except Exception as exc:
 
1231
            self._write_group = None
 
1232
            if not suppress_errors:
 
1233
                raise
 
1234
            mutter('abort_write_group failed')
 
1235
            log_exception_quietly()
 
1236
            note(gettext('bzr: ERROR (ignored): %s'), exc)
 
1237
        else:
 
1238
            if response != ('ok', ):
 
1239
                raise errors.UnexpectedSmartServerResponse(response)
 
1240
            self._write_group_tokens = None
725
1241
 
726
1242
    @property
727
1243
    def chk_bytes(self):
741
1257
        for older plugins that don't use e.g. the CommitBuilder
742
1258
        facility.
743
1259
        """
744
 
        self._ensure_real()
745
 
        return self._real_repository.commit_write_group()
 
1260
        if self._real_repository:
 
1261
            self._ensure_real()
 
1262
            return self._real_repository.commit_write_group()
 
1263
        if not self.is_in_write_group():
 
1264
            raise errors.BzrError("not in write group")
 
1265
        path = self.controldir._path_for_remote_call(self._client)
 
1266
        response = self._call('Repository.commit_write_group', path,
 
1267
            self._lock_token, self._write_group_tokens)
 
1268
        if response != ('ok', ):
 
1269
            raise errors.UnexpectedSmartServerResponse(response)
 
1270
        self._write_group_tokens = None
 
1271
        # Refresh data after writing to the repository.
 
1272
        self.refresh_data()
746
1273
 
747
1274
    def resume_write_group(self, tokens):
748
 
        self._ensure_real()
749
 
        return self._real_repository.resume_write_group(tokens)
 
1275
        if self._real_repository:
 
1276
            return self._real_repository.resume_write_group(tokens)
 
1277
        path = self.controldir._path_for_remote_call(self._client)
 
1278
        try:
 
1279
            response = self._call('Repository.check_write_group', path,
 
1280
               self._lock_token, tokens)
 
1281
        except errors.UnknownSmartMethod:
 
1282
            self._ensure_real()
 
1283
            return self._real_repository.resume_write_group(tokens)
 
1284
        if response != ('ok', ):
 
1285
            raise errors.UnexpectedSmartServerResponse(response)
 
1286
        self._write_group_tokens = tokens
750
1287
 
751
1288
    def suspend_write_group(self):
752
 
        self._ensure_real()
753
 
        return self._real_repository.suspend_write_group()
 
1289
        if self._real_repository:
 
1290
            return self._real_repository.suspend_write_group()
 
1291
        ret = self._write_group_tokens or []
 
1292
        self._write_group_tokens = None
 
1293
        return ret
754
1294
 
755
1295
    def get_missing_parent_inventories(self, check_for_missing_texts=True):
756
1296
        self._ensure_real()
764
1304
 
765
1305
    def get_rev_id_for_revno(self, revno, known_pair):
766
1306
        """See Repository.get_rev_id_for_revno."""
767
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
1307
        path = self.controldir._path_for_remote_call(self._client)
768
1308
        try:
769
1309
            if self._client._medium._is_remote_before((1, 17)):
770
1310
                return self._get_rev_id_for_revno_vfs(revno, known_pair)
807
1347
                warning('VFS Repository access triggered\n%s',
808
1348
                    ''.join(traceback.format_stack()))
809
1349
            self._unstacked_provider.missing_keys.clear()
810
 
            self.bzrdir._ensure_real()
 
1350
            self.controldir._ensure_real()
811
1351
            self._set_real_repository(
812
 
                self.bzrdir._real_bzrdir.open_repository())
 
1352
                self.controldir._real_bzrdir.open_repository())
813
1353
 
814
1354
    def _translate_error(self, err, **context):
815
 
        self.bzrdir._translate_error(err, repository=self, **context)
 
1355
        self.controldir._translate_error(err, repository=self, **context)
816
1356
 
817
1357
    def find_text_key_references(self):
818
1358
        """Find the text key references within the repository.
819
1359
 
820
 
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
821
 
        revision_ids. Each altered file-ids has the exact revision_ids that
822
 
        altered it listed explicitly.
823
1360
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
824
1361
            to whether they were referred to by the inventory of the
825
1362
            revision_id that they contain. The inventory texts from all present
843
1380
        """Private method for using with old (< 1.2) servers to fallback."""
844
1381
        if revision_id is None:
845
1382
            revision_id = ''
846
 
        elif revision.is_null(revision_id):
 
1383
        elif _mod_revision.is_null(revision_id):
847
1384
            return {}
848
1385
 
849
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
1386
        path = self.controldir._path_for_remote_call(self._client)
850
1387
        response = self._call_expecting_body(
851
1388
            'Repository.get_revision_graph', path, revision_id)
852
1389
        response_tuple, response_handler = response
873
1410
        return RemoteStreamSource(self, to_format)
874
1411
 
875
1412
    @needs_read_lock
 
1413
    def get_file_graph(self):
 
1414
        return graph.Graph(self.texts)
 
1415
 
 
1416
    @needs_read_lock
876
1417
    def has_revision(self, revision_id):
877
1418
        """True if this repository has a copy of the revision."""
878
 
        # Copy of bzrlib.repository.Repository.has_revision
 
1419
        # Copy of breezy.repository.Repository.has_revision
879
1420
        return revision_id in self.has_revisions((revision_id,))
880
1421
 
881
1422
    @needs_read_lock
885
1426
        :param revision_ids: An iterable of revision_ids.
886
1427
        :return: A set of the revision_ids that were present.
887
1428
        """
888
 
        # Copy of bzrlib.repository.Repository.has_revisions
 
1429
        # Copy of breezy.repository.Repository.has_revisions
889
1430
        parent_map = self.get_parent_map(revision_ids)
890
1431
        result = set(parent_map)
891
1432
        if _mod_revision.NULL_REVISION in revision_ids:
895
1436
    def _has_same_fallbacks(self, other_repo):
896
1437
        """Returns true if the repositories have the same fallbacks."""
897
1438
        # XXX: copied from Repository; it should be unified into a base class
898
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
 
1439
        # <https://bugs.launchpad.net/bzr/+bug/401622>
899
1440
        my_fb = self._fallback_repositories
900
1441
        other_fb = other_repo._fallback_repositories
901
1442
        if len(my_fb) != len(other_fb):
910
1451
        # one; unfortunately the tests rely on slightly different behaviour at
911
1452
        # present -- mbp 20090710
912
1453
        return (self.__class__ is other.__class__ and
913
 
                self.bzrdir.transport.base == other.bzrdir.transport.base)
 
1454
                self.controldir.transport.base == other.controldir.transport.base)
914
1455
 
915
1456
    def get_graph(self, other_repository=None):
916
1457
        """Return the graph for this repository format"""
928
1469
 
929
1470
    def gather_stats(self, revid=None, committers=None):
930
1471
        """See Repository.gather_stats()."""
931
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
1472
        path = self.controldir._path_for_remote_call(self._client)
932
1473
        # revid can be None to indicate no revisions, not just NULL_REVISION
933
 
        if revid is None or revision.is_null(revid):
 
1474
        if revid is None or _mod_revision.is_null(revid):
934
1475
            fmt_revid = ''
935
1476
        else:
936
1477
            fmt_revid = revid
953
1494
                result[key] = int(val_text)
954
1495
            elif key in ('firstrev', 'latestrev'):
955
1496
                values = val_text.split(' ')[1:]
956
 
                result[key] = (float(values[0]), long(values[1]))
 
1497
                result[key] = (float(values[0]), int(values[1]))
957
1498
 
958
1499
        return result
959
1500
 
965
1506
 
966
1507
    def get_physical_lock_status(self):
967
1508
        """See Repository.get_physical_lock_status()."""
968
 
        # should be an API call to the server.
969
 
        self._ensure_real()
970
 
        return self._real_repository.get_physical_lock_status()
 
1509
        path = self.controldir._path_for_remote_call(self._client)
 
1510
        try:
 
1511
            response = self._call('Repository.get_physical_lock_status', path)
 
1512
        except errors.UnknownSmartMethod:
 
1513
            self._ensure_real()
 
1514
            return self._real_repository.get_physical_lock_status()
 
1515
        if response[0] not in ('yes', 'no'):
 
1516
            raise errors.UnexpectedSmartServerResponse(response)
 
1517
        return (response[0] == 'yes')
971
1518
 
972
1519
    def is_in_write_group(self):
973
1520
        """Return True if there is an open write group.
974
1521
 
975
1522
        write groups are only applicable locally for the smart server..
976
1523
        """
 
1524
        if self._write_group_tokens is not None:
 
1525
            return True
977
1526
        if self._real_repository:
978
1527
            return self._real_repository.is_in_write_group()
979
1528
 
982
1531
 
983
1532
    def is_shared(self):
984
1533
        """See Repository.is_shared()."""
985
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
1534
        path = self.controldir._path_for_remote_call(self._client)
986
1535
        response = self._call('Repository.is_shared', path)
987
1536
        if response[0] not in ('yes', 'no'):
988
1537
            raise SmartProtocolError('unexpected response code %s' % (response,))
997
1546
        pass
998
1547
 
999
1548
    def lock_read(self):
 
1549
        """Lock the repository for read operations.
 
1550
 
 
1551
        :return: A breezy.lock.LogicalLockResult.
 
1552
        """
1000
1553
        # wrong eventually - want a local lock cache context
1001
1554
        if not self._lock_mode:
1002
1555
            self._note_lock('r')
1009
1562
                repo.lock_read()
1010
1563
        else:
1011
1564
            self._lock_count += 1
 
1565
        return lock.LogicalLockResult(self.unlock)
1012
1566
 
1013
1567
    def _remote_lock_write(self, token):
1014
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
1568
        path = self.controldir._path_for_remote_call(self._client)
1015
1569
        if token is None:
1016
1570
            token = ''
1017
1571
        err_context = {'token': token}
1054
1608
            raise errors.ReadOnlyError(self)
1055
1609
        else:
1056
1610
            self._lock_count += 1
1057
 
        return self._lock_token or None
 
1611
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
1058
1612
 
1059
1613
    def leave_lock_in_place(self):
1060
1614
        if not self._lock_token:
1109
1663
            self._real_repository.lock_write(self._lock_token)
1110
1664
        elif self._lock_mode == 'r':
1111
1665
            self._real_repository.lock_read()
 
1666
        if self._write_group_tokens is not None:
 
1667
            # if we are already in a write group, resume it
 
1668
            self._real_repository.resume_write_group(self._write_group_tokens)
 
1669
            self._write_group_tokens = None
1112
1670
 
1113
1671
    def start_write_group(self):
1114
1672
        """Start a write group on the decorated repository.
1118
1676
        for older plugins that don't use e.g. the CommitBuilder
1119
1677
        facility.
1120
1678
        """
1121
 
        self._ensure_real()
1122
 
        return self._real_repository.start_write_group()
 
1679
        if self._real_repository:
 
1680
            self._ensure_real()
 
1681
            return self._real_repository.start_write_group()
 
1682
        if not self.is_write_locked():
 
1683
            raise errors.NotWriteLocked(self)
 
1684
        if self._write_group_tokens is not None:
 
1685
            raise errors.BzrError('already in a write group')
 
1686
        path = self.controldir._path_for_remote_call(self._client)
 
1687
        try:
 
1688
            response = self._call('Repository.start_write_group', path,
 
1689
                self._lock_token)
 
1690
        except (errors.UnknownSmartMethod, errors.UnsuspendableWriteGroup):
 
1691
            self._ensure_real()
 
1692
            return self._real_repository.start_write_group()
 
1693
        if response[0] != 'ok':
 
1694
            raise errors.UnexpectedSmartServerResponse(response)
 
1695
        self._write_group_tokens = response[1]
1123
1696
 
1124
1697
    def _unlock(self, token):
1125
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
1698
        path = self.controldir._path_for_remote_call(self._client)
1126
1699
        if not token:
1127
1700
            # with no token the remote repository is not persistently locked.
1128
1701
            return
1152
1725
            # This is just to let the _real_repository stay up to date.
1153
1726
            if self._real_repository is not None:
1154
1727
                self._real_repository.unlock()
 
1728
            elif self._write_group_tokens is not None:
 
1729
                self.abort_write_group()
1155
1730
        finally:
1156
1731
            # The rpc-level lock should be released even if there was a
1157
1732
            # problem releasing the vfs-based lock.
1169
1744
 
1170
1745
    def break_lock(self):
1171
1746
        # should hand off to the network
1172
 
        self._ensure_real()
1173
 
        return self._real_repository.break_lock()
 
1747
        path = self.controldir._path_for_remote_call(self._client)
 
1748
        try:
 
1749
            response = self._call("Repository.break_lock", path)
 
1750
        except errors.UnknownSmartMethod:
 
1751
            self._ensure_real()
 
1752
            return self._real_repository.break_lock()
 
1753
        if response != ('ok',):
 
1754
            raise errors.UnexpectedSmartServerResponse(response)
1174
1755
 
1175
1756
    def _get_tarball(self, compression):
1176
1757
        """Return a TemporaryFile containing a repository tarball.
1178
1759
        Returns None if the server does not support sending tarballs.
1179
1760
        """
1180
1761
        import tempfile
1181
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
1762
        path = self.controldir._path_for_remote_call(self._client)
1182
1763
        try:
1183
1764
            response, protocol = self._call_expecting_body(
1184
1765
                'Repository.tarball', path, compression)
1194
1775
            return t
1195
1776
        raise errors.UnexpectedSmartServerResponse(response)
1196
1777
 
 
1778
    @needs_read_lock
1197
1779
    def sprout(self, to_bzrdir, revision_id=None):
1198
 
        # TODO: Option to control what format is created?
1199
 
        self._ensure_real()
1200
 
        dest_repo = self._real_repository._format.initialize(to_bzrdir,
1201
 
                                                             shared=False)
 
1780
        """Create a descendent repository for new development.
 
1781
 
 
1782
        Unlike clone, this does not copy the settings of the repository.
 
1783
        """
 
1784
        dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1202
1785
        dest_repo.fetch(self, revision_id=revision_id)
1203
1786
        return dest_repo
1204
1787
 
 
1788
    def _create_sprouting_repo(self, a_controldir, shared):
 
1789
        if not isinstance(a_controldir._format, self.controldir._format.__class__):
 
1790
            # use target default format.
 
1791
            dest_repo = a_controldir.create_repository()
 
1792
        else:
 
1793
            # Most control formats need the repository to be specifically
 
1794
            # created, but on some old all-in-one formats it's not needed
 
1795
            try:
 
1796
                dest_repo = self._format.initialize(a_controldir, shared=shared)
 
1797
            except errors.UninitializableFormat:
 
1798
                dest_repo = a_controldir.open_repository()
 
1799
        return dest_repo
 
1800
 
1205
1801
    ### These methods are just thin shims to the VFS object for now.
1206
1802
 
 
1803
    @needs_read_lock
1207
1804
    def revision_tree(self, revision_id):
1208
 
        self._ensure_real()
1209
 
        return self._real_repository.revision_tree(revision_id)
 
1805
        revision_id = _mod_revision.ensure_null(revision_id)
 
1806
        if revision_id == _mod_revision.NULL_REVISION:
 
1807
            return InventoryRevisionTree(self,
 
1808
                Inventory(root_id=None), _mod_revision.NULL_REVISION)
 
1809
        else:
 
1810
            return list(self.revision_trees([revision_id]))[0]
1210
1811
 
1211
1812
    def get_serializer_format(self):
1212
 
        self._ensure_real()
1213
 
        return self._real_repository.get_serializer_format()
 
1813
        path = self.controldir._path_for_remote_call(self._client)
 
1814
        try:
 
1815
            response = self._call('VersionedFileRepository.get_serializer_format',
 
1816
                path)
 
1817
        except errors.UnknownSmartMethod:
 
1818
            self._ensure_real()
 
1819
            return self._real_repository.get_serializer_format()
 
1820
        if response[0] != 'ok':
 
1821
            raise errors.UnexpectedSmartServerResponse(response)
 
1822
        return response[1]
1214
1823
 
1215
1824
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1825
                           timezone=None, committer=None, revprops=None,
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
 
1826
                           revision_id=None, lossy=False):
 
1827
        """Obtain a CommitBuilder for this repository.
 
1828
 
 
1829
        :param branch: Branch to commit to.
 
1830
        :param parents: Revision ids of the parents of the new revision.
 
1831
        :param config: Configuration to use.
 
1832
        :param timestamp: Optional timestamp recorded for commit.
 
1833
        :param timezone: Optional timezone for timestamp.
 
1834
        :param committer: Optional committer to set for commit.
 
1835
        :param revprops: Optional dictionary of revision properties.
 
1836
        :param revision_id: Optional revision id.
 
1837
        :param lossy: Whether to discard data that can not be natively
 
1838
            represented, when pushing to a foreign VCS
 
1839
        """
 
1840
        if self._fallback_repositories and not self._format.supports_chks:
 
1841
            raise errors.BzrError("Cannot commit directly to a stacked branch"
 
1842
                " in pre-2a formats. See "
 
1843
                "https://bugs.launchpad.net/bzr/+bug/375013 for details.")
 
1844
        if self._format.rich_root_data:
 
1845
            commit_builder_kls = vf_repository.VersionedFileRootCommitBuilder
 
1846
        else:
 
1847
            commit_builder_kls = vf_repository.VersionedFileCommitBuilder
 
1848
        result = commit_builder_kls(self, parents, config,
 
1849
            timestamp, timezone, committer, revprops, revision_id,
 
1850
            lossy)
 
1851
        self.start_write_group()
 
1852
        return result
1226
1853
 
1227
1854
    def add_fallback_repository(self, repository):
1228
1855
        """Add a repository to use for looking up data not held locally.
1235
1862
        # We need to accumulate additional repositories here, to pass them in
1236
1863
        # on various RPC's.
1237
1864
        #
 
1865
        # Make the check before we lock: this raises an exception.
 
1866
        self._check_fallback_repository(repository)
1238
1867
        if self.is_locked():
1239
1868
            # We will call fallback.unlock() when we transition to the unlocked
1240
1869
            # state, so always add a lock here. If a caller passes us a locked
1241
1870
            # repository, they are responsible for unlocking it later.
1242
1871
            repository.lock_read()
1243
 
        self._check_fallback_repository(repository)
1244
1872
        self._fallback_repositories.append(repository)
1245
1873
        # If self._real_repository was parameterised already (e.g. because a
1246
1874
        # _real_branch had its get_stacked_on_url method called), then the
1272
1900
            delta, new_revision_id, parents, basis_inv=basis_inv,
1273
1901
            propagate_caches=propagate_caches)
1274
1902
 
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)
 
1903
    def add_revision(self, revision_id, rev, inv=None):
 
1904
        _mod_revision.check_not_reserved_id(revision_id)
 
1905
        key = (revision_id,)
 
1906
        # check inventory present
 
1907
        if not self.inventories.get_parent_map([key]):
 
1908
            if inv is None:
 
1909
                raise errors.WeaveRevisionNotPresent(revision_id,
 
1910
                                                     self.inventories)
 
1911
            else:
 
1912
                # yes, this is not suitable for adding with ghosts.
 
1913
                rev.inventory_sha1 = self.add_inventory(revision_id, inv,
 
1914
                                                        rev.parent_ids)
 
1915
        else:
 
1916
            rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
 
1917
        self._add_revision(rev)
 
1918
 
 
1919
    def _add_revision(self, rev):
 
1920
        if self._real_repository is not None:
 
1921
            return self._real_repository._add_revision(rev)
 
1922
        text = self._serializer.write_revision_to_string(rev)
 
1923
        key = (rev.revision_id,)
 
1924
        parents = tuple((parent,) for parent in rev.parent_ids)
 
1925
        self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
 
1926
            [('revisions', [FulltextContentFactory(key, parents, None, text)])],
 
1927
            self._format, self._write_group_tokens)
1279
1928
 
1280
1929
    @needs_read_lock
1281
1930
    def get_inventory(self, revision_id):
 
1931
        return list(self.iter_inventories([revision_id]))[0]
 
1932
 
 
1933
    def _iter_inventories_rpc(self, revision_ids, ordering):
 
1934
        if ordering is None:
 
1935
            ordering = 'unordered'
 
1936
        path = self.controldir._path_for_remote_call(self._client)
 
1937
        body = "\n".join(revision_ids)
 
1938
        response_tuple, response_handler = (
 
1939
            self._call_with_body_bytes_expecting_body(
 
1940
                "VersionedFileRepository.get_inventories",
 
1941
                (path, ordering), body))
 
1942
        if response_tuple[0] != "ok":
 
1943
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
1944
        deserializer = inventory_delta.InventoryDeltaDeserializer()
 
1945
        byte_stream = response_handler.read_streamed_body()
 
1946
        decoded = smart_repo._byte_stream_to_stream(byte_stream)
 
1947
        if decoded is None:
 
1948
            # no results whatsoever
 
1949
            return
 
1950
        src_format, stream = decoded
 
1951
        if src_format.network_name() != self._format.network_name():
 
1952
            raise AssertionError(
 
1953
                "Mismatched RemoteRepository and stream src %r, %r" % (
 
1954
                src_format.network_name(), self._format.network_name()))
 
1955
        # ignore the src format, it's not really relevant
 
1956
        prev_inv = Inventory(root_id=None,
 
1957
            revision_id=_mod_revision.NULL_REVISION)
 
1958
        # there should be just one substream, with inventory deltas
 
1959
        substream_kind, substream = next(stream)
 
1960
        if substream_kind != "inventory-deltas":
 
1961
            raise AssertionError(
 
1962
                 "Unexpected stream %r received" % substream_kind)
 
1963
        for record in substream:
 
1964
            (parent_id, new_id, versioned_root, tree_references, invdelta) = (
 
1965
                deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
 
1966
            if parent_id != prev_inv.revision_id:
 
1967
                raise AssertionError("invalid base %r != %r" % (parent_id,
 
1968
                    prev_inv.revision_id))
 
1969
            inv = prev_inv.create_by_apply_delta(invdelta, new_id)
 
1970
            yield inv, inv.revision_id
 
1971
            prev_inv = inv
 
1972
 
 
1973
    def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
1974
        self._ensure_real()
1283
 
        return self._real_repository.get_inventory(revision_id)
 
1975
        return self._real_repository._iter_inventories(revision_ids, ordering)
1284
1976
 
1285
1977
    def iter_inventories(self, revision_ids, ordering=None):
1286
 
        self._ensure_real()
1287
 
        return self._real_repository.iter_inventories(revision_ids, ordering)
 
1978
        """Get many inventories by revision_ids.
 
1979
 
 
1980
        This will buffer some or all of the texts used in constructing the
 
1981
        inventories in memory, but will only parse a single inventory at a
 
1982
        time.
 
1983
 
 
1984
        :param revision_ids: The expected revision ids of the inventories.
 
1985
        :param ordering: optional ordering, e.g. 'topological'.  If not
 
1986
            specified, the order of revision_ids will be preserved (by
 
1987
            buffering if necessary).
 
1988
        :return: An iterator of inventories.
 
1989
        """
 
1990
        if ((None in revision_ids)
 
1991
            or (_mod_revision.NULL_REVISION in revision_ids)):
 
1992
            raise ValueError('cannot get null revision inventory')
 
1993
        for inv, revid in self._iter_inventories(revision_ids, ordering):
 
1994
            if inv is None:
 
1995
                raise errors.NoSuchRevision(self, revid)
 
1996
            yield inv
 
1997
 
 
1998
    def _iter_inventories(self, revision_ids, ordering=None):
 
1999
        if len(revision_ids) == 0:
 
2000
            return
 
2001
        missing = set(revision_ids)
 
2002
        if ordering is None:
 
2003
            order_as_requested = True
 
2004
            invs = {}
 
2005
            order = list(revision_ids)
 
2006
            order.reverse()
 
2007
            next_revid = order.pop()
 
2008
        else:
 
2009
            order_as_requested = False
 
2010
            if ordering != 'unordered' and self._fallback_repositories:
 
2011
                raise ValueError('unsupported ordering %r' % ordering)
 
2012
        iter_inv_fns = [self._iter_inventories_rpc] + [
 
2013
            fallback._iter_inventories for fallback in
 
2014
            self._fallback_repositories]
 
2015
        try:
 
2016
            for iter_inv in iter_inv_fns:
 
2017
                request = [revid for revid in revision_ids if revid in missing]
 
2018
                for inv, revid in iter_inv(request, ordering):
 
2019
                    if inv is None:
 
2020
                        continue
 
2021
                    missing.remove(inv.revision_id)
 
2022
                    if ordering != 'unordered':
 
2023
                        invs[revid] = inv
 
2024
                    else:
 
2025
                        yield inv, revid
 
2026
                if order_as_requested:
 
2027
                    # Yield as many results as we can while preserving order.
 
2028
                    while next_revid in invs:
 
2029
                        inv = invs.pop(next_revid)
 
2030
                        yield inv, inv.revision_id
 
2031
                        try:
 
2032
                            next_revid = order.pop()
 
2033
                        except IndexError:
 
2034
                            # We still want to fully consume the stream, just
 
2035
                            # in case it is not actually finished at this point
 
2036
                            next_revid = None
 
2037
                            break
 
2038
        except errors.UnknownSmartMethod:
 
2039
            for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
 
2040
                yield inv, revid
 
2041
            return
 
2042
        # Report missing
 
2043
        if order_as_requested:
 
2044
            if next_revid is not None:
 
2045
                yield None, next_revid
 
2046
            while order:
 
2047
                revid = order.pop()
 
2048
                yield invs.get(revid), revid
 
2049
        else:
 
2050
            while missing:
 
2051
                yield None, missing.pop()
1288
2052
 
1289
2053
    @needs_read_lock
1290
2054
    def get_revision(self, revision_id):
1291
 
        self._ensure_real()
1292
 
        return self._real_repository.get_revision(revision_id)
 
2055
        return self.get_revisions([revision_id])[0]
1293
2056
 
1294
2057
    def get_transaction(self):
1295
2058
        self._ensure_real()
1296
2059
        return self._real_repository.get_transaction()
1297
2060
 
1298
2061
    @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)
 
2062
    def clone(self, a_controldir, revision_id=None):
 
2063
        dest_repo = self._create_sprouting_repo(
 
2064
            a_controldir, shared=self.is_shared())
 
2065
        self.copy_content_into(dest_repo, revision_id)
 
2066
        return dest_repo
1302
2067
 
1303
2068
    def make_working_trees(self):
1304
2069
        """See Repository.make_working_trees"""
1305
 
        self._ensure_real()
1306
 
        return self._real_repository.make_working_trees()
 
2070
        path = self.controldir._path_for_remote_call(self._client)
 
2071
        try:
 
2072
            response = self._call('Repository.make_working_trees', path)
 
2073
        except errors.UnknownSmartMethod:
 
2074
            self._ensure_real()
 
2075
            return self._real_repository.make_working_trees()
 
2076
        if response[0] not in ('yes', 'no'):
 
2077
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
2078
        return response[0] == 'yes'
1307
2079
 
1308
2080
    def refresh_data(self):
1309
 
        """Re-read any data needed to to synchronise with disk.
 
2081
        """Re-read any data needed to synchronise with disk.
1310
2082
 
1311
2083
        This method is intended to be called after another repository instance
1312
2084
        (such as one used by a smart server) has inserted data into the
1313
 
        repository. It may not be called during a write group, but may be
1314
 
        called at any other time.
 
2085
        repository. On all repositories this will work outside of write groups.
 
2086
        Some repository formats (pack and newer for breezy native formats)
 
2087
        support refresh_data inside write groups. If called inside a write
 
2088
        group on a repository that does not support refreshing in a write group
 
2089
        IsInWriteGroupError will be raised.
1315
2090
        """
1316
 
        if self.is_in_write_group():
1317
 
            raise errors.InternalBzrError(
1318
 
                "May not refresh_data while in a write group.")
1319
2091
        if self._real_repository is not None:
1320
2092
            self._real_repository.refresh_data()
 
2093
        # Refresh the parents cache for this object
 
2094
        self._unstacked_provider.disable_cache()
 
2095
        self._unstacked_provider.enable_cache()
1321
2096
 
1322
2097
    def revision_ids_to_search_result(self, result_set):
1323
2098
        """Convert a set of revision ids to a graph SearchResult."""
1324
2099
        result_parents = set()
1325
 
        for parents in self.get_graph().get_parent_map(
1326
 
            result_set).itervalues():
 
2100
        for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2101
            result_parents.update(parents)
1328
2102
        included_keys = result_set.intersection(result_parents)
1329
2103
        start_keys = result_set.difference(included_keys)
1330
2104
        exclude_keys = result_parents.difference(result_set)
1331
 
        result = graph.SearchResult(start_keys, exclude_keys,
 
2105
        result = vf_search.SearchResult(start_keys, exclude_keys,
1332
2106
            len(result_set), result_set)
1333
2107
        return result
1334
2108
 
1335
2109
    @needs_read_lock
1336
 
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
2110
    def search_missing_revision_ids(self, other,
 
2111
            find_ghosts=True, revision_ids=None, if_present_ids=None,
 
2112
            limit=None):
1337
2113
        """Return the revision ids that other has that this does not.
1338
2114
 
1339
2115
        These are returned in topological order.
1340
2116
 
1341
2117
        revision_id: only return revision ids included by revision_id.
1342
2118
        """
1343
 
        return repository.InterRepository.get(
1344
 
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
 
2119
        inter_repo = _mod_repository.InterRepository.get(other, self)
 
2120
        return inter_repo.search_missing_revision_ids(
 
2121
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
2122
            if_present_ids=if_present_ids, limit=limit)
1345
2123
 
1346
 
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
 
2124
    def fetch(self, source, revision_id=None, find_ghosts=False,
1347
2125
            fetch_spec=None):
1348
2126
        # No base implementation to use as RemoteRepository is not a subclass
1349
2127
        # of Repository; so this is a copy of Repository.fetch().
1360
2138
            # check that last_revision is in 'from' and then return a
1361
2139
            # no-operation.
1362
2140
            if (revision_id is not None and
1363
 
                not revision.is_null(revision_id)):
 
2141
                not _mod_revision.is_null(revision_id)):
1364
2142
                self.get_revision(revision_id)
1365
2143
            return 0, []
1366
2144
        # if there is no specific appropriate InterRepository, this will get
1367
2145
        # the InterRepository base class, which raises an
1368
2146
        # IncompatibleRepositories when asked to fetch.
1369
 
        inter = repository.InterRepository.get(source, self)
1370
 
        return inter.fetch(revision_id=revision_id, pb=pb,
 
2147
        inter = _mod_repository.InterRepository.get(source, self)
 
2148
        if (fetch_spec is not None and
 
2149
            not getattr(inter, "supports_fetch_spec", False)):
 
2150
            raise errors.UnsupportedOperation(
 
2151
                "fetch_spec not supported for %r" % inter)
 
2152
        return inter.fetch(revision_id=revision_id,
1371
2153
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1372
2154
 
1373
2155
    def create_bundle(self, target, base, fileobj, format=None):
1374
2156
        self._ensure_real()
1375
2157
        self._real_repository.create_bundle(target, base, fileobj, format)
1376
2158
 
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
 
 
1382
2159
    def fileids_altered_by_revision_ids(self, revision_ids):
1383
2160
        self._ensure_real()
1384
2161
        return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
1388
2165
        return self._real_repository._get_versioned_file_checker(
1389
2166
            revisions, revision_versions_cache)
1390
2167
 
 
2168
    def _iter_files_bytes_rpc(self, desired_files, absent):
 
2169
        path = self.controldir._path_for_remote_call(self._client)
 
2170
        lines = []
 
2171
        identifiers = []
 
2172
        for (file_id, revid, identifier) in desired_files:
 
2173
            lines.append("%s\0%s" % (
 
2174
                osutils.safe_file_id(file_id),
 
2175
                osutils.safe_revision_id(revid)))
 
2176
            identifiers.append(identifier)
 
2177
        (response_tuple, response_handler) = (
 
2178
            self._call_with_body_bytes_expecting_body(
 
2179
            "Repository.iter_files_bytes", (path, ), "\n".join(lines)))
 
2180
        if response_tuple != ('ok', ):
 
2181
            response_handler.cancel_read_body()
 
2182
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2183
        byte_stream = response_handler.read_streamed_body()
 
2184
        def decompress_stream(start, byte_stream, unused):
 
2185
            decompressor = zlib.decompressobj()
 
2186
            yield decompressor.decompress(start)
 
2187
            while decompressor.unused_data == "":
 
2188
                try:
 
2189
                    data = next(byte_stream)
 
2190
                except StopIteration:
 
2191
                    break
 
2192
                yield decompressor.decompress(data)
 
2193
            yield decompressor.flush()
 
2194
            unused.append(decompressor.unused_data)
 
2195
        unused = ""
 
2196
        while True:
 
2197
            while not "\n" in unused:
 
2198
                unused += next(byte_stream)
 
2199
            header, rest = unused.split("\n", 1)
 
2200
            args = header.split("\0")
 
2201
            if args[0] == "absent":
 
2202
                absent[identifiers[int(args[3])]] = (args[1], args[2])
 
2203
                unused = rest
 
2204
                continue
 
2205
            elif args[0] == "ok":
 
2206
                idx = int(args[1])
 
2207
            else:
 
2208
                raise errors.UnexpectedSmartServerResponse(args)
 
2209
            unused_chunks = []
 
2210
            yield (identifiers[idx],
 
2211
                decompress_stream(rest, byte_stream, unused_chunks))
 
2212
            unused = "".join(unused_chunks)
 
2213
 
1391
2214
    def iter_files_bytes(self, desired_files):
1392
2215
        """See Repository.iter_file_bytes.
1393
2216
        """
1394
 
        self._ensure_real()
1395
 
        return self._real_repository.iter_files_bytes(desired_files)
 
2217
        try:
 
2218
            absent = {}
 
2219
            for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
 
2220
                    desired_files, absent):
 
2221
                yield identifier, bytes_iterator
 
2222
            for fallback in self._fallback_repositories:
 
2223
                if not absent:
 
2224
                    break
 
2225
                desired_files = [(key[0], key[1], identifier)
 
2226
                    for identifier, key in viewitems(absent)]
 
2227
                for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
 
2228
                    del absent[identifier]
 
2229
                    yield identifier, bytes_iterator
 
2230
            if absent:
 
2231
                # There may be more missing items, but raise an exception
 
2232
                # for just one.
 
2233
                missing_identifier = next(iter(absent))
 
2234
                missing_key = absent[missing_identifier]
 
2235
                raise errors.RevisionNotPresent(revision_id=missing_key[1],
 
2236
                    file_id=missing_key[0])
 
2237
        except errors.UnknownSmartMethod:
 
2238
            self._ensure_real()
 
2239
            for (identifier, bytes_iterator) in (
 
2240
                self._real_repository.iter_files_bytes(desired_files)):
 
2241
                yield identifier, bytes_iterator
 
2242
 
 
2243
    def get_cached_parent_map(self, revision_ids):
 
2244
        """See breezy.CachingParentsProvider.get_cached_parent_map"""
 
2245
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
1396
2246
 
1397
2247
    def get_parent_map(self, revision_ids):
1398
 
        """See bzrlib.Graph.get_parent_map()."""
 
2248
        """See breezy.Graph.get_parent_map()."""
1399
2249
        return self._make_parents_provider().get_parent_map(revision_ids)
1400
2250
 
1401
2251
    def _get_parent_map_rpc(self, keys):
1420
2270
            # There is one other "bug" which is that ghosts in
1421
2271
            # get_revision_graph() are not returned at all. But we won't worry
1422
2272
            # about that for now.
1423
 
            for node_id, parent_ids in rg.iteritems():
 
2273
            for node_id, parent_ids in viewitems(rg):
1424
2274
                if parent_ids == ():
1425
2275
                    rg[node_id] = (NULL_REVISION,)
1426
2276
            rg[NULL_REVISION] = ()
1457
2307
        if parents_map is None:
1458
2308
            # Repository is not locked, so there's no cache.
1459
2309
            parents_map = {}
1460
 
        # start_set is all the keys in the cache
1461
 
        start_set = set(parents_map)
1462
 
        # result set is all the references to keys in the cache
1463
 
        result_parents = set()
1464
 
        for parents in parents_map.itervalues():
1465
 
            result_parents.update(parents)
1466
 
        stop_keys = result_parents.difference(start_set)
1467
 
        # We don't need to send ghosts back to the server as a position to
1468
 
        # stop either.
1469
 
        stop_keys.difference_update(self._unstacked_provider.missing_keys)
1470
 
        key_count = len(parents_map)
1471
 
        if (NULL_REVISION in result_parents
1472
 
            and NULL_REVISION in self._unstacked_provider.missing_keys):
1473
 
            # If we pruned NULL_REVISION from the stop_keys because it's also
1474
 
            # in our cache of "missing" keys we need to increment our key count
1475
 
            # by 1, because the reconsitituted SearchResult on the server will
1476
 
            # still consider NULL_REVISION to be an included key.
1477
 
            key_count += 1
1478
 
        included_keys = start_set.intersection(result_parents)
1479
 
        start_set.difference_update(included_keys)
 
2310
        if _DEFAULT_SEARCH_DEPTH <= 0:
 
2311
            (start_set, stop_keys,
 
2312
             key_count) = vf_search.search_result_from_parent_map(
 
2313
                parents_map, self._unstacked_provider.missing_keys)
 
2314
        else:
 
2315
            (start_set, stop_keys,
 
2316
             key_count) = vf_search.limited_search_result_from_parent_map(
 
2317
                parents_map, self._unstacked_provider.missing_keys,
 
2318
                keys, depth=_DEFAULT_SEARCH_DEPTH)
1480
2319
        recipe = ('manual', start_set, stop_keys, key_count)
1481
2320
        body = self._serialise_search_recipe(recipe)
1482
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
2321
        path = self.controldir._path_for_remote_call(self._client)
1483
2322
        for key in keys:
1484
 
            if type(key) is not str:
 
2323
            if not isinstance(key, str):
1485
2324
                raise ValueError(
1486
2325
                    "key %r not a plain string" % (key,))
1487
2326
        verb = 'Repository.get_parent_map'
1531
2370
 
1532
2371
    @needs_read_lock
1533
2372
    def get_signature_text(self, revision_id):
1534
 
        self._ensure_real()
1535
 
        return self._real_repository.get_signature_text(revision_id)
 
2373
        path = self.controldir._path_for_remote_call(self._client)
 
2374
        try:
 
2375
            response_tuple, response_handler = self._call_expecting_body(
 
2376
                'Repository.get_revision_signature_text', path, revision_id)
 
2377
        except errors.UnknownSmartMethod:
 
2378
            self._ensure_real()
 
2379
            return self._real_repository.get_signature_text(revision_id)
 
2380
        except errors.NoSuchRevision as err:
 
2381
            for fallback in self._fallback_repositories:
 
2382
                try:
 
2383
                    return fallback.get_signature_text(revision_id)
 
2384
                except errors.NoSuchRevision:
 
2385
                    pass
 
2386
            raise err
 
2387
        else:
 
2388
            if response_tuple[0] != 'ok':
 
2389
                raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2390
            return response_handler.read_body_bytes()
1536
2391
 
1537
2392
    @needs_read_lock
1538
2393
    def _get_inventory_xml(self, revision_id):
 
2394
        # This call is used by older working tree formats,
 
2395
        # which stored a serialized basis inventory.
1539
2396
        self._ensure_real()
1540
2397
        return self._real_repository._get_inventory_xml(revision_id)
1541
2398
 
 
2399
    @needs_write_lock
1542
2400
    def reconcile(self, other=None, thorough=False):
1543
 
        self._ensure_real()
1544
 
        return self._real_repository.reconcile(other=other, thorough=thorough)
 
2401
        from ..reconcile import RepoReconciler
 
2402
        path = self.controldir._path_for_remote_call(self._client)
 
2403
        try:
 
2404
            response, handler = self._call_expecting_body(
 
2405
                'Repository.reconcile', path, self._lock_token)
 
2406
        except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
 
2407
            self._ensure_real()
 
2408
            return self._real_repository.reconcile(other=other, thorough=thorough)
 
2409
        if response != ('ok', ):
 
2410
            raise errors.UnexpectedSmartServerResponse(response)
 
2411
        body = handler.read_body_bytes()
 
2412
        result = RepoReconciler(self)
 
2413
        for line in body.split('\n'):
 
2414
            if not line:
 
2415
                continue
 
2416
            key, val_text = line.split(':')
 
2417
            if key == "garbage_inventories":
 
2418
                result.garbage_inventories = int(val_text)
 
2419
            elif key == "inconsistent_parents":
 
2420
                result.inconsistent_parents = int(val_text)
 
2421
            else:
 
2422
                mutter("unknown reconcile key %r" % key)
 
2423
        return result
1545
2424
 
1546
2425
    def all_revision_ids(self):
1547
 
        self._ensure_real()
1548
 
        return self._real_repository.all_revision_ids()
 
2426
        path = self.controldir._path_for_remote_call(self._client)
 
2427
        try:
 
2428
            response_tuple, response_handler = self._call_expecting_body(
 
2429
                "Repository.all_revision_ids", path)
 
2430
        except errors.UnknownSmartMethod:
 
2431
            self._ensure_real()
 
2432
            return self._real_repository.all_revision_ids()
 
2433
        if response_tuple != ("ok", ):
 
2434
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2435
        revids = set(response_handler.read_body_bytes().splitlines())
 
2436
        for fallback in self._fallback_repositories:
 
2437
            revids.update(set(fallback.all_revision_ids()))
 
2438
        return list(revids)
 
2439
 
 
2440
    def _filtered_revision_trees(self, revision_ids, file_ids):
 
2441
        """Return Tree for a revision on this branch with only some files.
 
2442
 
 
2443
        :param revision_ids: a sequence of revision-ids;
 
2444
          a revision-id may not be None or 'null:'
 
2445
        :param file_ids: if not None, the result is filtered
 
2446
          so that only those file-ids, their parents and their
 
2447
          children are included.
 
2448
        """
 
2449
        inventories = self.iter_inventories(revision_ids)
 
2450
        for inv in inventories:
 
2451
            # Should we introduce a FilteredRevisionTree class rather
 
2452
            # than pre-filter the inventory here?
 
2453
            filtered_inv = inv.filter(file_ids)
 
2454
            yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1549
2455
 
1550
2456
    @needs_read_lock
1551
2457
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1552
 
        self._ensure_real()
1553
 
        return self._real_repository.get_deltas_for_revisions(revisions,
1554
 
            specific_fileids=specific_fileids)
 
2458
        medium = self._client._medium
 
2459
        if medium._is_remote_before((1, 2)):
 
2460
            self._ensure_real()
 
2461
            for delta in self._real_repository.get_deltas_for_revisions(
 
2462
                    revisions, specific_fileids):
 
2463
                yield delta
 
2464
            return
 
2465
        # Get the revision-ids of interest
 
2466
        required_trees = set()
 
2467
        for revision in revisions:
 
2468
            required_trees.add(revision.revision_id)
 
2469
            required_trees.update(revision.parent_ids[:1])
 
2470
 
 
2471
        # Get the matching filtered trees. Note that it's more
 
2472
        # efficient to pass filtered trees to changes_from() rather
 
2473
        # than doing the filtering afterwards. changes_from() could
 
2474
        # arguably do the filtering itself but it's path-based, not
 
2475
        # file-id based, so filtering before or afterwards is
 
2476
        # currently easier.
 
2477
        if specific_fileids is None:
 
2478
            trees = dict((t.get_revision_id(), t) for
 
2479
                t in self.revision_trees(required_trees))
 
2480
        else:
 
2481
            trees = dict((t.get_revision_id(), t) for
 
2482
                t in self._filtered_revision_trees(required_trees,
 
2483
                specific_fileids))
 
2484
 
 
2485
        # Calculate the deltas
 
2486
        for revision in revisions:
 
2487
            if not revision.parent_ids:
 
2488
                old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
 
2489
            else:
 
2490
                old_tree = trees[revision.parent_ids[0]]
 
2491
            yield trees[revision.revision_id].changes_from(old_tree)
1555
2492
 
1556
2493
    @needs_read_lock
1557
2494
    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)
 
2495
        r = self.get_revision(revision_id)
 
2496
        return list(self.get_deltas_for_revisions([r],
 
2497
            specific_fileids=specific_fileids))[0]
1561
2498
 
1562
2499
    @needs_read_lock
1563
2500
    def revision_trees(self, revision_ids):
1564
 
        self._ensure_real()
1565
 
        return self._real_repository.revision_trees(revision_ids)
 
2501
        inventories = self.iter_inventories(revision_ids)
 
2502
        for inv in inventories:
 
2503
            yield InventoryRevisionTree(self, inv, inv.revision_id)
1566
2504
 
1567
2505
    @needs_read_lock
1568
2506
    def get_revision_reconcile(self, revision_id):
1576
2514
            callback_refs=callback_refs, check_repo=check_repo)
1577
2515
 
1578
2516
    def copy_content_into(self, destination, revision_id=None):
1579
 
        self._ensure_real()
1580
 
        return self._real_repository.copy_content_into(
1581
 
            destination, revision_id=revision_id)
 
2517
        """Make a complete copy of the content in self into destination.
 
2518
 
 
2519
        This is a destructive operation! Do not use it on existing
 
2520
        repositories.
 
2521
        """
 
2522
        interrepo = _mod_repository.InterRepository.get(self, destination)
 
2523
        return interrepo.copy_content(revision_id)
1582
2524
 
1583
2525
    def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2526
        # get a tarball of the remote repository, and copy from that into the
1585
2527
        # destination
1586
 
        from bzrlib import osutils
1587
2528
        import tarfile
1588
2529
        # TODO: Maybe a progress bar while streaming the tarball?
1589
 
        note("Copying repository content as tarball...")
 
2530
        note(gettext("Copying repository content as tarball..."))
1590
2531
        tar_file = self._get_tarball('bz2')
1591
2532
        if tar_file is None:
1592
2533
            return None
1596
2537
                mode='r|bz2')
1597
2538
            tmpdir = osutils.mkdtemp()
1598
2539
            try:
1599
 
                _extract_tar(tar, tmpdir)
1600
 
                tmp_bzrdir = BzrDir.open(tmpdir)
 
2540
                tar.extractall(tmpdir)
 
2541
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1601
2542
                tmp_repo = tmp_bzrdir.open_repository()
1602
2543
                tmp_repo.copy_content_into(destination, revision_id)
1603
2544
            finally:
1621
2562
    @needs_write_lock
1622
2563
    def pack(self, hint=None, clean_obsolete_packs=False):
1623
2564
        """Compress the data within the repository.
1624
 
 
1625
 
        This is not currently implemented within the smart server.
1626
2565
        """
1627
 
        self._ensure_real()
1628
 
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
 
2566
        if hint is None:
 
2567
            body = ""
 
2568
        else:
 
2569
            body = "".join([l+"\n" for l in hint])
 
2570
        path = self.controldir._path_for_remote_call(self._client)
 
2571
        try:
 
2572
            response, handler = self._call_with_body_bytes_expecting_body(
 
2573
                'Repository.pack', (path, self._lock_token,
 
2574
                    str(clean_obsolete_packs)), body)
 
2575
        except errors.UnknownSmartMethod:
 
2576
            self._ensure_real()
 
2577
            return self._real_repository.pack(hint=hint,
 
2578
                clean_obsolete_packs=clean_obsolete_packs)
 
2579
        handler.cancel_read_body()
 
2580
        if response != ('ok', ):
 
2581
            raise errors.UnexpectedSmartServerResponse(response)
1629
2582
 
1630
2583
    @property
1631
2584
    def revisions(self):
1632
2585
        """Decorate the real repository for now.
1633
2586
 
1634
 
        In the short term this should become a real object to intercept graph
1635
 
        lookups.
1636
 
 
1637
2587
        In the long term a full blown network facility is needed.
1638
2588
        """
1639
2589
        self._ensure_real()
1644
2594
            new_value_str = "True"
1645
2595
        else:
1646
2596
            new_value_str = "False"
1647
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
2597
        path = self.controldir._path_for_remote_call(self._client)
1648
2598
        try:
1649
2599
            response = self._call(
1650
2600
                'Repository.set_make_working_trees', path, new_value_str)
1667
2617
 
1668
2618
    @needs_write_lock
1669
2619
    def sign_revision(self, revision_id, gpg_strategy):
1670
 
        self._ensure_real()
1671
 
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
 
2620
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2621
        plaintext = testament.as_short_text()
 
2622
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1672
2623
 
1673
2624
    @property
1674
2625
    def texts(self):
1680
2631
        self._ensure_real()
1681
2632
        return self._real_repository.texts
1682
2633
 
 
2634
    def _iter_revisions_rpc(self, revision_ids):
 
2635
        body = "\n".join(revision_ids)
 
2636
        path = self.controldir._path_for_remote_call(self._client)
 
2637
        response_tuple, response_handler = (
 
2638
            self._call_with_body_bytes_expecting_body(
 
2639
            "Repository.iter_revisions", (path, ), body))
 
2640
        if response_tuple[0] != "ok":
 
2641
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
2642
        serializer_format = response_tuple[1]
 
2643
        serializer = serializer_format_registry.get(serializer_format)
 
2644
        byte_stream = response_handler.read_streamed_body()
 
2645
        decompressor = zlib.decompressobj()
 
2646
        chunks = []
 
2647
        for bytes in byte_stream:
 
2648
            chunks.append(decompressor.decompress(bytes))
 
2649
            if decompressor.unused_data != "":
 
2650
                chunks.append(decompressor.flush())
 
2651
                yield serializer.read_revision_from_string("".join(chunks))
 
2652
                unused = decompressor.unused_data
 
2653
                decompressor = zlib.decompressobj()
 
2654
                chunks = [decompressor.decompress(unused)]
 
2655
        chunks.append(decompressor.flush())
 
2656
        text = "".join(chunks)
 
2657
        if text != "":
 
2658
            yield serializer.read_revision_from_string("".join(chunks))
 
2659
 
1683
2660
    @needs_read_lock
1684
 
    def get_revisions(self, revision_ids):
1685
 
        self._ensure_real()
1686
 
        return self._real_repository.get_revisions(revision_ids)
 
2661
    def iter_revisions(self, revision_ids):
 
2662
        for rev_id in revision_ids:
 
2663
            if not rev_id or not isinstance(rev_id, bytes):
 
2664
                raise errors.InvalidRevisionId(
 
2665
                    revision_id=rev_id, branch=self)
 
2666
        try:
 
2667
            missing = set(revision_ids)
 
2668
            for rev in self._iter_revisions_rpc(revision_ids):
 
2669
                missing.remove(rev.revision_id)
 
2670
                yield (rev.revision_id, rev)
 
2671
            for fallback in self._fallback_repositories:
 
2672
                if not missing:
 
2673
                    break
 
2674
                for (revid, rev) in fallback.iter_revisions(missing):
 
2675
                    if rev is not None:
 
2676
                        yield (revid, rev)
 
2677
                        missing.remove(revid)
 
2678
            for revid in missing:
 
2679
                yield (revid, None)
 
2680
        except errors.UnknownSmartMethod:
 
2681
            self._ensure_real()
 
2682
            for entry in self._real_repository.iter_revisions(revision_ids):
 
2683
                yield entry
1687
2684
 
1688
2685
    def supports_rich_root(self):
1689
2686
        return self._format.rich_root_data
1690
2687
 
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
 
 
1695
2688
    @property
1696
2689
    def _serializer(self):
1697
2690
        return self._format._serializer
1698
2691
 
 
2692
    @needs_write_lock
1699
2693
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1700
 
        self._ensure_real()
1701
 
        return self._real_repository.store_revision_signature(
1702
 
            gpg_strategy, plaintext, revision_id)
 
2694
        signature = gpg_strategy.sign(plaintext)
 
2695
        self.add_signature_text(revision_id, signature)
1703
2696
 
1704
2697
    def add_signature_text(self, revision_id, signature):
1705
 
        self._ensure_real()
1706
 
        return self._real_repository.add_signature_text(revision_id, signature)
 
2698
        if self._real_repository:
 
2699
            # If there is a real repository the write group will
 
2700
            # be in the real repository as well, so use that:
 
2701
            self._ensure_real()
 
2702
            return self._real_repository.add_signature_text(
 
2703
                revision_id, signature)
 
2704
        path = self.controldir._path_for_remote_call(self._client)
 
2705
        response, handler = self._call_with_body_bytes_expecting_body(
 
2706
            'Repository.add_signature_text', (path, self._lock_token,
 
2707
                revision_id) + tuple(self._write_group_tokens), signature)
 
2708
        handler.cancel_read_body()
 
2709
        self.refresh_data()
 
2710
        if response[0] != 'ok':
 
2711
            raise errors.UnexpectedSmartServerResponse(response)
 
2712
        self._write_group_tokens = response[1:]
1707
2713
 
1708
2714
    def has_signature_for_revision_id(self, revision_id):
1709
 
        self._ensure_real()
1710
 
        return self._real_repository.has_signature_for_revision_id(revision_id)
 
2715
        path = self.controldir._path_for_remote_call(self._client)
 
2716
        try:
 
2717
            response = self._call('Repository.has_signature_for_revision_id',
 
2718
                path, revision_id)
 
2719
        except errors.UnknownSmartMethod:
 
2720
            self._ensure_real()
 
2721
            return self._real_repository.has_signature_for_revision_id(
 
2722
                revision_id)
 
2723
        if response[0] not in ('yes', 'no'):
 
2724
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
2725
        if response[0] == 'yes':
 
2726
            return True
 
2727
        for fallback in self._fallback_repositories:
 
2728
            if fallback.has_signature_for_revision_id(revision_id):
 
2729
                return True
 
2730
        return False
 
2731
 
 
2732
    @needs_read_lock
 
2733
    def verify_revision_signature(self, revision_id, gpg_strategy):
 
2734
        if not self.has_signature_for_revision_id(revision_id):
 
2735
            return gpg.SIGNATURE_NOT_SIGNED, None
 
2736
        signature = self.get_signature_text(revision_id)
 
2737
 
 
2738
        testament = _mod_testament.Testament.from_revision(self, revision_id)
 
2739
        plaintext = testament.as_short_text()
 
2740
 
 
2741
        return gpg_strategy.verify(signature, plaintext)
1711
2742
 
1712
2743
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2744
        self._ensure_real()
1714
2745
        return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2746
            _files_pb=_files_pb)
1716
2747
 
1717
 
    def revision_graph_can_have_wrong_parents(self):
1718
 
        # The answer depends on the remote repo format.
1719
 
        self._ensure_real()
1720
 
        return self._real_repository.revision_graph_can_have_wrong_parents()
1721
 
 
1722
2748
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2749
        self._ensure_real()
1724
2750
        return self._real_repository._find_inconsistent_revision_parents(
1732
2758
        providers = [self._unstacked_provider]
1733
2759
        if other is not None:
1734
2760
            providers.insert(0, other)
1735
 
        providers.extend(r._make_parents_provider() for r in
1736
 
                         self._fallback_repositories)
1737
 
        return graph.StackedParentsProvider(providers)
 
2761
        return graph.StackedParentsProvider(_LazyListJoin(
 
2762
            providers, self._fallback_repositories))
1738
2763
 
1739
2764
    def _serialise_search_recipe(self, recipe):
1740
2765
        """Serialise a graph search recipe.
1748
2773
        return '\n'.join((start_keys, stop_keys, count))
1749
2774
 
1750
2775
    def _serialise_search_result(self, search_result):
1751
 
        if isinstance(search_result, graph.PendingAncestryResult):
1752
 
            parts = ['ancestry-of']
1753
 
            parts.extend(search_result.heads)
1754
 
        else:
1755
 
            recipe = search_result.get_recipe()
1756
 
            parts = [recipe[0], self._serialise_search_recipe(recipe)]
 
2776
        parts = search_result.get_network_struct()
1757
2777
        return '\n'.join(parts)
1758
2778
 
1759
2779
    def autopack(self):
1760
 
        path = self.bzrdir._path_for_remote_call(self._client)
 
2780
        path = self.controldir._path_for_remote_call(self._client)
1761
2781
        try:
1762
2782
            response = self._call('PackRepository.autopack', path)
1763
2783
        except errors.UnknownSmartMethod:
1769
2789
            raise errors.UnexpectedSmartServerResponse(response)
1770
2790
 
1771
2791
 
1772
 
class RemoteStreamSink(repository.StreamSink):
 
2792
class RemoteStreamSink(vf_repository.StreamSink):
1773
2793
 
1774
2794
    def _insert_real(self, stream, src_format, resume_tokens):
1775
2795
        self.target_repo._ensure_real()
1791
2811
            lock_args = ()
1792
2812
        client = target._client
1793
2813
        medium = client._medium
1794
 
        path = target.bzrdir._path_for_remote_call(client)
 
2814
        path = target.controldir._path_for_remote_call(client)
1795
2815
        # Probe for the verb to use with an empty stream before sending the
1796
2816
        # real stream to it.  We do this both to avoid the risk of sending a
1797
2817
        # large request that is then rejected, and because we don't want to
1876
2896
        self._last_substream and self._last_stream so that the stream can be
1877
2897
        resumed by _resume_stream_with_vfs.
1878
2898
        """
1879
 
                    
 
2899
 
1880
2900
        stream_iter = iter(stream)
1881
2901
        for substream_kind, substream in stream_iter:
1882
2902
            if substream_kind == 'inventory-deltas':
1885
2905
                return
1886
2906
            else:
1887
2907
                yield substream_kind, substream
1888
 
            
1889
 
 
1890
 
class RemoteStreamSource(repository.StreamSource):
 
2908
 
 
2909
 
 
2910
class RemoteStreamSource(vf_repository.StreamSource):
1891
2911
    """Stream data from a remote server."""
1892
2912
 
1893
2913
    def get_stream(self, search):
1914
2934
 
1915
2935
    def _real_stream(self, repo, search):
1916
2936
        """Get a stream for search from repo.
1917
 
        
1918
 
        This never called RemoteStreamSource.get_stream, and is a heler
1919
 
        for RemoteStreamSource._get_stream to allow getting a stream 
 
2937
 
 
2938
        This never called RemoteStreamSource.get_stream, and is a helper
 
2939
        for RemoteStreamSource._get_stream to allow getting a stream
1920
2940
        reliably whether fallback back because of old servers or trying
1921
2941
        to stream from a non-RemoteRepository (which the stacked support
1922
2942
        code will do).
1947
2967
            return self._real_stream(repo, search)
1948
2968
        client = repo._client
1949
2969
        medium = client._medium
1950
 
        path = repo.bzrdir._path_for_remote_call(client)
 
2970
        path = repo.controldir._path_for_remote_call(client)
1951
2971
        search_bytes = repo._serialise_search_result(search)
1952
2972
        args = (path, self.to_format.network_name())
1953
2973
        candidate_verbs = [
1954
2974
            ('Repository.get_stream_1.19', (1, 19)),
1955
2975
            ('Repository.get_stream', (1, 13))]
 
2976
 
1956
2977
        found_verb = False
1957
2978
        for verb, version in candidate_verbs:
1958
2979
            if medium._is_remote_before(version):
1962
2983
                    verb, args, search_bytes)
1963
2984
            except errors.UnknownSmartMethod:
1964
2985
                medium._remember_remote_is_before(version)
 
2986
            except errors.UnknownErrorFromSmartServer as e:
 
2987
                if isinstance(search, vf_search.EverythingResult):
 
2988
                    error_verb = e.error_from_smart_server.error_verb
 
2989
                    if error_verb == 'BadSearch':
 
2990
                        # Pre-2.4 servers don't support this sort of search.
 
2991
                        # XXX: perhaps falling back to VFS on BadSearch is a
 
2992
                        # good idea in general?  It might provide a little bit
 
2993
                        # of protection against client-side bugs.
 
2994
                        medium._remember_remote_is_before((2, 4))
 
2995
                        break
 
2996
                raise
1965
2997
            else:
1966
2998
                response_tuple, response_handler = response
1967
2999
                found_verb = True
1971
3003
        if response_tuple[0] != 'ok':
1972
3004
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1973
3005
        byte_stream = response_handler.read_streamed_body()
1974
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
 
3006
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
 
3007
            self._record_counter)
1975
3008
        if src_format.network_name() != repo._format.network_name():
1976
3009
            raise AssertionError(
1977
3010
                "Mismatched RemoteRepository and stream src %r, %r" % (
2021
3054
    """
2022
3055
 
2023
3056
    def __init__(self, bzrdir, _client):
2024
 
        self.bzrdir = bzrdir
 
3057
        self.controldir = bzrdir
2025
3058
        self._client = _client
2026
3059
        self._need_find_modes = True
2027
3060
        LockableFiles.__init__(
2038
3071
 
2039
3072
    def __init__(self, network_name=None):
2040
3073
        super(RemoteBranchFormat, self).__init__()
2041
 
        self._matchingbzrdir = RemoteBzrDirFormat()
2042
 
        self._matchingbzrdir.set_branch_format(self)
 
3074
        self._matchingcontroldir = RemoteBzrDirFormat()
 
3075
        self._matchingcontroldir.set_branch_format(self)
2043
3076
        self._custom_format = None
2044
3077
        self._network_name = network_name
2045
3078
 
2049
3082
 
2050
3083
    def _ensure_real(self):
2051
3084
        if self._custom_format is None:
2052
 
            self._custom_format = branch.network_format_registry.get(
2053
 
                self._network_name)
 
3085
            try:
 
3086
                self._custom_format = branch.network_format_registry.get(
 
3087
                    self._network_name)
 
3088
            except KeyError:
 
3089
                raise errors.UnknownFormatError(kind='branch',
 
3090
                    format=self._network_name)
2054
3091
 
2055
3092
    def get_format_description(self):
2056
3093
        self._ensure_real()
2059
3096
    def network_name(self):
2060
3097
        return self._network_name
2061
3098
 
2062
 
    def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
 
        return a_bzrdir.open_branch(name=name, 
 
3099
    def open(self, a_controldir, name=None, ignore_fallbacks=False):
 
3100
        return a_controldir.open_branch(name=name, 
2064
3101
            ignore_fallbacks=ignore_fallbacks)
2065
3102
 
2066
 
    def _vfs_initialize(self, a_bzrdir, name):
 
3103
    def _vfs_initialize(self, a_controldir, name, append_revisions_only,
 
3104
                        repository=None):
2067
3105
        # Initialisation when using a local bzrdir object, or a non-vfs init
2068
3106
        # method is not available on the server.
2069
3107
        # self._custom_format is always set - the start of initialize ensures
2070
3108
        # that.
2071
 
        if isinstance(a_bzrdir, RemoteBzrDir):
2072
 
            a_bzrdir._ensure_real()
2073
 
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2074
 
                name)
 
3109
        if isinstance(a_controldir, RemoteBzrDir):
 
3110
            a_controldir._ensure_real()
 
3111
            result = self._custom_format.initialize(a_controldir._real_bzrdir,
 
3112
                name=name, append_revisions_only=append_revisions_only,
 
3113
                repository=repository)
2075
3114
        else:
2076
3115
            # We assume the bzrdir is parameterised; it may not be.
2077
 
            result = self._custom_format.initialize(a_bzrdir, name)
2078
 
        if (isinstance(a_bzrdir, RemoteBzrDir) and
 
3116
            result = self._custom_format.initialize(a_controldir, name=name,
 
3117
                append_revisions_only=append_revisions_only,
 
3118
                repository=repository)
 
3119
        if (isinstance(a_controldir, RemoteBzrDir) and
2079
3120
            not isinstance(result, RemoteBranch)):
2080
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
 
3121
            result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2081
3122
                                  name=name)
2082
3123
        return result
2083
3124
 
2084
 
    def initialize(self, a_bzrdir, name=None):
 
3125
    def initialize(self, a_controldir, name=None, repository=None,
 
3126
                   append_revisions_only=None):
 
3127
        if name is None:
 
3128
            name = a_controldir._get_selected_branch()
2085
3129
        # 1) get the network name to use.
2086
3130
        if self._custom_format:
2087
3131
            network_name = self._custom_format.network_name()
2088
3132
        else:
2089
 
            # Select the current bzrlib default and ask for that.
2090
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
3133
            # Select the current breezy default and ask for that.
 
3134
            reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3135
            reference_format = reference_bzrdir_format.get_branch_format()
2092
3136
            self._custom_format = reference_format
2093
3137
            network_name = reference_format.network_name()
2094
3138
        # Being asked to create on a non RemoteBzrDir:
2095
 
        if not isinstance(a_bzrdir, RemoteBzrDir):
2096
 
            return self._vfs_initialize(a_bzrdir, name=name)
2097
 
        medium = a_bzrdir._client._medium
 
3139
        if not isinstance(a_controldir, RemoteBzrDir):
 
3140
            return self._vfs_initialize(a_controldir, name=name,
 
3141
                append_revisions_only=append_revisions_only,
 
3142
                repository=repository)
 
3143
        medium = a_controldir._client._medium
2098
3144
        if medium._is_remote_before((1, 13)):
2099
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
3145
            return self._vfs_initialize(a_controldir, name=name,
 
3146
                append_revisions_only=append_revisions_only,
 
3147
                repository=repository)
2100
3148
        # Creating on a remote bzr dir.
2101
3149
        # 2) try direct creation via RPC
2102
 
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
 
        if name is not None:
 
3150
        path = a_controldir._path_for_remote_call(a_controldir._client)
 
3151
        if name != "":
2104
3152
            # XXX JRV20100304: Support creating colocated branches
2105
3153
            raise errors.NoColocatedBranchSupport(self)
2106
3154
        verb = 'BzrDir.create_branch'
2107
3155
        try:
2108
 
            response = a_bzrdir._call(verb, path, network_name)
 
3156
            response = a_controldir._call(verb, path, network_name)
2109
3157
        except errors.UnknownSmartMethod:
2110
3158
            # Fallback - use vfs methods
2111
3159
            medium._remember_remote_is_before((1, 13))
2112
 
            return self._vfs_initialize(a_bzrdir, name=name)
 
3160
            return self._vfs_initialize(a_controldir, name=name,
 
3161
                    append_revisions_only=append_revisions_only,
 
3162
                    repository=repository)
2113
3163
        if response[0] != 'ok':
2114
3164
            raise errors.UnexpectedSmartServerResponse(response)
2115
3165
        # Turn the response into a RemoteRepository object.
2116
3166
        format = RemoteBranchFormat(network_name=response[1])
2117
3167
        repo_format = response_tuple_to_repo_format(response[3:])
2118
 
        if response[2] == '':
2119
 
            repo_bzrdir = a_bzrdir
 
3168
        repo_path = response[2]
 
3169
        if repository is not None:
 
3170
            remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
 
3171
            url_diff = urlutils.relative_url(repository.user_url,
 
3172
                    remote_repo_url)
 
3173
            if url_diff != '.':
 
3174
                raise AssertionError(
 
3175
                    'repository.user_url %r does not match URL from server '
 
3176
                    'response (%r + %r)'
 
3177
                    % (repository.user_url, a_controldir.user_url, repo_path))
 
3178
            remote_repo = repository
2120
3179
        else:
2121
 
            repo_bzrdir = RemoteBzrDir(
2122
 
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2123
 
                a_bzrdir._client)
2124
 
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2125
 
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
 
3180
            if repo_path == '':
 
3181
                repo_bzrdir = a_controldir
 
3182
            else:
 
3183
                repo_bzrdir = RemoteBzrDir(
 
3184
                    a_controldir.root_transport.clone(repo_path), a_controldir._format,
 
3185
                    a_controldir._client)
 
3186
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
3187
        remote_branch = RemoteBranch(a_controldir, remote_repo,
2126
3188
            format=format, setup_stacking=False, name=name)
 
3189
        if append_revisions_only:
 
3190
            remote_branch.set_append_revisions_only(append_revisions_only)
2127
3191
        # XXX: We know this is a new branch, so it must have revno 0, revid
2128
3192
        # NULL_REVISION. Creating the branch locked would make this be unable
2129
3193
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3212
        self._ensure_real()
2149
3213
        return self._custom_format.supports_set_append_revisions_only()
2150
3214
 
 
3215
    def _use_default_local_heads_to_fetch(self):
 
3216
        # If the branch format is a metadir format *and* its heads_to_fetch
 
3217
        # implementation is not overridden vs the base class, we can use the
 
3218
        # base class logic rather than use the heads_to_fetch RPC.  This is
 
3219
        # usually cheaper in terms of net round trips, as the last-revision and
 
3220
        # tags info fetched is cached and would be fetched anyway.
 
3221
        self._ensure_real()
 
3222
        if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
 
3223
            branch_class = self._custom_format._branch_class()
 
3224
            heads_to_fetch_impl = branch_class.heads_to_fetch.__func__
 
3225
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.__func__:
 
3226
                return True
 
3227
        return False
 
3228
 
 
3229
 
 
3230
class RemoteBranchStore(_mod_config.IniFileStore):
 
3231
    """Branch store which attempts to use HPSS calls to retrieve branch store.
 
3232
 
 
3233
    Note that this is specific to bzr-based formats.
 
3234
    """
 
3235
 
 
3236
    def __init__(self, branch):
 
3237
        super(RemoteBranchStore, self).__init__()
 
3238
        self.branch = branch
 
3239
        self.id = "branch"
 
3240
        self._real_store = None
 
3241
 
 
3242
    def external_url(self):
 
3243
        return urlutils.join(self.branch.user_url, 'branch.conf')
 
3244
 
 
3245
    def _load_content(self):
 
3246
        path = self.branch._remote_path()
 
3247
        try:
 
3248
            response, handler = self.branch._call_expecting_body(
 
3249
                'Branch.get_config_file', path)
 
3250
        except errors.UnknownSmartMethod:
 
3251
            self._ensure_real()
 
3252
            return self._real_store._load_content()
 
3253
        if len(response) and response[0] != 'ok':
 
3254
            raise errors.UnexpectedSmartServerResponse(response)
 
3255
        return handler.read_body_bytes()
 
3256
 
 
3257
    def _save_content(self, content):
 
3258
        path = self.branch._remote_path()
 
3259
        try:
 
3260
            response, handler = self.branch._call_with_body_bytes_expecting_body(
 
3261
                'Branch.put_config_file', (path,
 
3262
                    self.branch._lock_token, self.branch._repo_lock_token),
 
3263
                content)
 
3264
        except errors.UnknownSmartMethod:
 
3265
            self._ensure_real()
 
3266
            return self._real_store._save_content(content)
 
3267
        handler.cancel_read_body()
 
3268
        if response != ('ok', ):
 
3269
            raise errors.UnexpectedSmartServerResponse(response)
 
3270
 
 
3271
    def _ensure_real(self):
 
3272
        self.branch._ensure_real()
 
3273
        if self._real_store is None:
 
3274
            self._real_store = _mod_config.BranchStore(self.branch)
 
3275
 
2151
3276
 
2152
3277
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3278
    """Branch stored on a server accessed by HPSS RPC.
2156
3281
    """
2157
3282
 
2158
3283
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2159
 
        _client=None, format=None, setup_stacking=True, name=None):
 
3284
        _client=None, format=None, setup_stacking=True, name=None,
 
3285
        possible_transports=None):
2160
3286
        """Create a RemoteBranch instance.
2161
3287
 
2162
3288
        :param real_branch: An optional local implementation of the branch
2173
3299
        # We intentionally don't call the parent class's __init__, because it
2174
3300
        # will try to assign to self.tags, which is a property in this subclass.
2175
3301
        # And the parent's __init__ doesn't do much anyway.
2176
 
        self.bzrdir = remote_bzrdir
 
3302
        self.controldir = remote_bzrdir
 
3303
        self.name = name
2177
3304
        if _client is not None:
2178
3305
            self._client = _client
2179
3306
        else:
2191
3318
            self._real_branch.repository = self.repository
2192
3319
        else:
2193
3320
            self._real_branch = None
2194
 
        # Fill out expected attributes of branch for bzrlib API users.
 
3321
        # Fill out expected attributes of branch for breezy API users.
2195
3322
        self._clear_cached_state()
2196
3323
        # TODO: deprecate self.base in favor of user_url
2197
 
        self.base = self.bzrdir.user_url
 
3324
        self.base = self.controldir.user_url
2198
3325
        self._name = name
2199
3326
        self._control_files = None
2200
3327
        self._lock_mode = None
2202
3329
        self._repo_lock_token = None
2203
3330
        self._lock_count = 0
2204
3331
        self._leave_lock = False
 
3332
        self.conf_store = None
2205
3333
        # Setup a format: note that we cannot call _ensure_real until all the
2206
3334
        # attributes above are set: This code cannot be moved higher up in this
2207
3335
        # function.
2227
3355
            hook(self)
2228
3356
        self._is_stacked = False
2229
3357
        if setup_stacking:
2230
 
            self._setup_stacking()
 
3358
            self._setup_stacking(possible_transports)
2231
3359
 
2232
 
    def _setup_stacking(self):
 
3360
    def _setup_stacking(self, possible_transports):
2233
3361
        # configure stacking into the remote repository, by reading it from
2234
3362
        # the vfs branch.
2235
3363
        try:
2236
3364
            fallback_url = self.get_stacked_on_url()
2237
 
        except (errors.NotStacked, errors.UnstackableBranchFormat,
2238
 
            errors.UnstackableRepositoryFormat), e:
 
3365
        except (errors.NotStacked, branch.UnstackableBranchFormat,
 
3366
            errors.UnstackableRepositoryFormat) as e:
2239
3367
            return
2240
3368
        self._is_stacked = True
2241
 
        self._activate_fallback_location(fallback_url)
 
3369
        if possible_transports is None:
 
3370
            possible_transports = []
 
3371
        else:
 
3372
            possible_transports = list(possible_transports)
 
3373
        possible_transports.append(self.controldir.root_transport)
 
3374
        self._activate_fallback_location(fallback_url,
 
3375
            possible_transports=possible_transports)
2242
3376
 
2243
3377
    def _get_config(self):
2244
3378
        return RemoteBranchConfig(self)
2245
3379
 
 
3380
    def _get_config_store(self):
 
3381
        if self.conf_store is None:
 
3382
            self.conf_store =  RemoteBranchStore(self)
 
3383
        return self.conf_store
 
3384
 
 
3385
    def store_uncommitted(self, creator):
 
3386
        self._ensure_real()
 
3387
        return self._real_branch.store_uncommitted(creator)
 
3388
 
 
3389
    def get_unshelver(self, tree):
 
3390
        self._ensure_real()
 
3391
        return self._real_branch.get_unshelver(tree)
 
3392
 
2246
3393
    def _get_real_transport(self):
2247
3394
        # if we try vfs access, return the real branch's vfs transport
2248
3395
        self._ensure_real()
2264
3411
            if not vfs.vfs_enabled():
2265
3412
                raise AssertionError('smart server vfs must be enabled '
2266
3413
                    'to use vfs implementation')
2267
 
            self.bzrdir._ensure_real()
2268
 
            self._real_branch = self.bzrdir._real_bzrdir.open_branch(
 
3414
            self.controldir._ensure_real()
 
3415
            self._real_branch = self.controldir._real_bzrdir.open_branch(
2269
3416
                ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
 
3417
            # The remote branch and the real branch shares the same store. If
 
3418
            # we don't, there will always be cases where one of the stores
 
3419
            # doesn't see an update made on the other.
 
3420
            self._real_branch.conf_store = self.conf_store
2270
3421
            if self.repository._real_repository is None:
2271
3422
                # Give the remote repository the matching real repo.
2272
3423
                real_repo = self._real_branch.repository
2308
3459
        # because it triggers an _ensure_real that we otherwise might not need.
2309
3460
        if self._control_files is None:
2310
3461
            self._control_files = RemoteBranchLockableFiles(
2311
 
                self.bzrdir, self._client)
 
3462
                self.controldir, self._client)
2312
3463
        return self._control_files
2313
3464
 
2314
 
    def _get_checkout_format(self):
2315
 
        self._ensure_real()
2316
 
        return self._real_branch._get_checkout_format()
2317
 
 
2318
3465
    def get_physical_lock_status(self):
2319
3466
        """See Branch.get_physical_lock_status()."""
2320
 
        # should be an API call to the server, as branches must be lockable.
2321
 
        self._ensure_real()
2322
 
        return self._real_branch.get_physical_lock_status()
 
3467
        try:
 
3468
            response = self._client.call('Branch.get_physical_lock_status',
 
3469
                self._remote_path())
 
3470
        except errors.UnknownSmartMethod:
 
3471
            self._ensure_real()
 
3472
            return self._real_branch.get_physical_lock_status()
 
3473
        if response[0] not in ('yes', 'no'):
 
3474
            raise errors.UnexpectedSmartServerResponse(response)
 
3475
        return (response[0] == 'yes')
2323
3476
 
2324
3477
    def get_stacked_on_url(self):
2325
3478
        """Get the URL this branch is stacked against.
2335
3488
            # self._translate_error, so we can't use self._call either.
2336
3489
            response = self._client.call('Branch.get_stacked_on_url',
2337
3490
                self._remote_path())
2338
 
        except errors.ErrorFromSmartServer, err:
 
3491
        except errors.ErrorFromSmartServer as err:
2339
3492
            # there may not be a repository yet, so we can't call through
2340
3493
            # its _translate_error
2341
3494
            _translate_error(err, branch=self)
2342
 
        except errors.UnknownSmartMethod, err:
 
3495
        except errors.UnknownSmartMethod as err:
2343
3496
            self._ensure_real()
2344
3497
            return self._real_branch.get_stacked_on_url()
2345
3498
        if response[0] != 'ok':
2348
3501
 
2349
3502
    def set_stacked_on_url(self, url):
2350
3503
        branch.Branch.set_stacked_on_url(self, url)
 
3504
        # We need the stacked_on_url to be visible both locally (to not query
 
3505
        # it repeatedly) and remotely (so smart verbs can get it server side)
 
3506
        # Without the following line,
 
3507
        # breezy.tests.per_branch.test_create_clone.TestCreateClone
 
3508
        # .test_create_clone_on_transport_stacked_hooks_get_stacked_branch
 
3509
        # fails for remote branches -- vila 2012-01-04
 
3510
        self.conf_store.save_changes()
2351
3511
        if not url:
2352
3512
            self._is_stacked = False
2353
3513
        else:
2354
3514
            self._is_stacked = True
2355
 
        
 
3515
 
2356
3516
    def _vfs_get_tags_bytes(self):
2357
3517
        self._ensure_real()
2358
3518
        return self._real_branch._get_tags_bytes()
2359
3519
 
 
3520
    @needs_read_lock
2360
3521
    def _get_tags_bytes(self):
 
3522
        if self._tags_bytes is None:
 
3523
            self._tags_bytes = self._get_tags_bytes_via_hpss()
 
3524
        return self._tags_bytes
 
3525
 
 
3526
    def _get_tags_bytes_via_hpss(self):
2361
3527
        medium = self._client._medium
2362
3528
        if medium._is_remote_before((1, 13)):
2363
3529
            return self._vfs_get_tags_bytes()
2373
3539
        return self._real_branch._set_tags_bytes(bytes)
2374
3540
 
2375
3541
    def _set_tags_bytes(self, bytes):
 
3542
        if self.is_locked():
 
3543
            self._tags_bytes = bytes
2376
3544
        medium = self._client._medium
2377
3545
        if medium._is_remote_before((1, 18)):
2378
3546
            self._vfs_set_tags_bytes(bytes)
2387
3555
            self._vfs_set_tags_bytes(bytes)
2388
3556
 
2389
3557
    def lock_read(self):
 
3558
        """Lock the branch for read operations.
 
3559
 
 
3560
        :return: A breezy.lock.LogicalLockResult.
 
3561
        """
2390
3562
        self.repository.lock_read()
2391
3563
        if not self._lock_mode:
2392
3564
            self._note_lock('r')
2396
3568
                self._real_branch.lock_read()
2397
3569
        else:
2398
3570
            self._lock_count += 1
 
3571
        return lock.LogicalLockResult(self.unlock)
2399
3572
 
2400
3573
    def _remote_lock_write(self, token):
2401
3574
        if token is None:
2402
3575
            branch_token = repo_token = ''
2403
3576
        else:
2404
3577
            branch_token = token
2405
 
            repo_token = self.repository.lock_write()
 
3578
            repo_token = self.repository.lock_write().repository_token
2406
3579
            self.repository.unlock()
2407
3580
        err_context = {'token': token}
2408
 
        response = self._call(
2409
 
            'Branch.lock_write', self._remote_path(), branch_token,
2410
 
            repo_token or '', **err_context)
 
3581
        try:
 
3582
            response = self._call(
 
3583
                'Branch.lock_write', self._remote_path(), branch_token,
 
3584
                repo_token or '', **err_context)
 
3585
        except errors.LockContention as e:
 
3586
            # The LockContention from the server doesn't have any
 
3587
            # information about the lock_url. We re-raise LockContention
 
3588
            # with valid lock_url.
 
3589
            raise errors.LockContention('(remote lock)',
 
3590
                self.repository.base.split('.bzr/')[0])
2411
3591
        if response[0] != 'ok':
2412
3592
            raise errors.UnexpectedSmartServerResponse(response)
2413
3593
        ok, branch_token, repo_token = response
2434
3614
            self._lock_mode = 'w'
2435
3615
            self._lock_count = 1
2436
3616
        elif self._lock_mode == 'r':
2437
 
            raise errors.ReadOnlyTransaction
 
3617
            raise errors.ReadOnlyError(self)
2438
3618
        else:
2439
3619
            if token is not None:
2440
3620
                # A token was given to lock_write, and we're relocking, so
2445
3625
            self._lock_count += 1
2446
3626
            # Re-lock the repository too.
2447
3627
            self.repository.lock_write(self._repo_lock_token)
2448
 
        return self._lock_token or None
 
3628
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
2449
3629
 
2450
3630
    def _unlock(self, branch_token, repo_token):
2451
3631
        err_context = {'token': str((branch_token, repo_token))}
2461
3641
        try:
2462
3642
            self._lock_count -= 1
2463
3643
            if not self._lock_count:
 
3644
                if self.conf_store is not None:
 
3645
                    self.conf_store.save_changes()
2464
3646
                self._clear_cached_state()
2465
3647
                mode = self._lock_mode
2466
3648
                self._lock_mode = None
2489
3671
            self.repository.unlock()
2490
3672
 
2491
3673
    def break_lock(self):
2492
 
        self._ensure_real()
2493
 
        return self._real_branch.break_lock()
 
3674
        try:
 
3675
            response = self._call(
 
3676
                'Branch.break_lock', self._remote_path())
 
3677
        except errors.UnknownSmartMethod:
 
3678
            self._ensure_real()
 
3679
            return self._real_branch.break_lock()
 
3680
        if response != ('ok',):
 
3681
            raise errors.UnexpectedSmartServerResponse(response)
2494
3682
 
2495
3683
    def leave_lock_in_place(self):
2496
3684
        if not self._lock_token:
2520
3708
            missing_parent = parent_map[missing_parent]
2521
3709
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2522
3710
 
2523
 
    def _last_revision_info(self):
 
3711
    def _read_last_revision_info(self):
2524
3712
        response = self._call('Branch.last_revision_info', self._remote_path())
2525
3713
        if response[0] != 'ok':
2526
3714
            raise SmartProtocolError('unexpected response code %s' % (response,))
2543
3731
        return result
2544
3732
 
2545
3733
    def _remote_path(self):
2546
 
        return self.bzrdir._path_for_remote_call(self._client)
 
3734
        return self.controldir._path_for_remote_call(self._client)
2547
3735
 
2548
3736
    def _set_last_revision_descendant(self, revision_id, other_branch,
2549
3737
            allow_diverged=False, allow_overwrite_descendant=False):
2589
3777
            raise errors.UnexpectedSmartServerResponse(response)
2590
3778
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2591
3779
 
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
 
 
2606
3780
    def _get_parent_location(self):
2607
3781
        medium = self._client._medium
2608
3782
        if medium._is_remote_before((1, 13)):
2629
3803
            return self._vfs_set_parent_location(url)
2630
3804
        try:
2631
3805
            call_url = url or ''
2632
 
            if type(call_url) is not str:
 
3806
            if not isinstance(call_url, str):
2633
3807
                raise AssertionError('url must be a str or None (%s)' % url)
2634
3808
            response = self._call('Branch.set_parent_location',
2635
3809
                self._remote_path(), self._lock_token, self._repo_lock_token,
2654
3828
            _override_hook_target=self, **kwargs)
2655
3829
 
2656
3830
    @needs_read_lock
2657
 
    def push(self, target, overwrite=False, stop_revision=None):
 
3831
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
2658
3832
        self._ensure_real()
2659
3833
        return self._real_branch.push(
2660
 
            target, overwrite=overwrite, stop_revision=stop_revision,
 
3834
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
2661
3835
            _override_hook_source_branch=self)
2662
3836
 
 
3837
    def peek_lock_mode(self):
 
3838
        return self._lock_mode
 
3839
 
2663
3840
    def is_locked(self):
2664
3841
        return self._lock_count >= 1
2665
3842
 
2666
3843
    @needs_read_lock
 
3844
    def revision_id_to_dotted_revno(self, revision_id):
 
3845
        """Given a revision id, return its dotted revno.
 
3846
 
 
3847
        :return: a tuple like (1,) or (400,1,3).
 
3848
        """
 
3849
        try:
 
3850
            response = self._call('Branch.revision_id_to_revno',
 
3851
                self._remote_path(), revision_id)
 
3852
        except errors.UnknownSmartMethod:
 
3853
            self._ensure_real()
 
3854
            return self._real_branch.revision_id_to_dotted_revno(revision_id)
 
3855
        if response[0] == 'ok':
 
3856
            return tuple([int(x) for x in response[1:]])
 
3857
        else:
 
3858
            raise errors.UnexpectedSmartServerResponse(response)
 
3859
 
 
3860
    @needs_read_lock
2667
3861
    def revision_id_to_revno(self, revision_id):
2668
 
        self._ensure_real()
2669
 
        return self._real_branch.revision_id_to_revno(revision_id)
 
3862
        """Given a revision id on the branch mainline, return its revno.
 
3863
 
 
3864
        :return: an integer
 
3865
        """
 
3866
        try:
 
3867
            response = self._call('Branch.revision_id_to_revno',
 
3868
                self._remote_path(), revision_id)
 
3869
        except errors.UnknownSmartMethod:
 
3870
            self._ensure_real()
 
3871
            return self._real_branch.revision_id_to_revno(revision_id)
 
3872
        if response[0] == 'ok':
 
3873
            if len(response) == 2:
 
3874
                return int(response[1])
 
3875
            raise NoSuchRevision(self, revision_id)
 
3876
        else:
 
3877
            raise errors.UnexpectedSmartServerResponse(response)
2670
3878
 
2671
3879
    @needs_write_lock
2672
3880
    def set_last_revision_info(self, revno, revision_id):
2673
3881
        # XXX: These should be returned by the set_last_revision_info verb
2674
3882
        old_revno, old_revid = self.last_revision_info()
2675
3883
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
 
        revision_id = ensure_null(revision_id)
 
3884
        if not revision_id or not isinstance(revision_id, bytes):
 
3885
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2677
3886
        try:
2678
3887
            response = self._call('Branch.set_last_revision_info',
2679
3888
                self._remote_path(), self._lock_token, self._repo_lock_token,
2708
3917
            except errors.UnknownSmartMethod:
2709
3918
                medium._remember_remote_is_before((1, 6))
2710
3919
        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))
 
3920
        graph = self.repository.get_graph()
 
3921
        (last_revno, last_revid) = self.last_revision_info()
 
3922
        known_revision_ids = [
 
3923
            (last_revid, last_revno),
 
3924
            (_mod_revision.NULL_REVISION, 0),
 
3925
            ]
 
3926
        if last_rev is not None:
 
3927
            if not graph.is_ancestor(last_rev, revision_id):
 
3928
                # our previous tip is not merged into stop_revision
 
3929
                raise errors.DivergedBranches(self, other_branch)
 
3930
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
 
3931
        self.set_last_revision_info(revno, revision_id)
2713
3932
 
2714
3933
    def set_push_location(self, location):
 
3934
        self._set_config_location('push_location', location)
 
3935
 
 
3936
    def heads_to_fetch(self):
 
3937
        if self._format._use_default_local_heads_to_fetch():
 
3938
            # We recognise this format, and its heads-to-fetch implementation
 
3939
            # is the default one (tip + tags).  In this case it's cheaper to
 
3940
            # just use the default implementation rather than a special RPC as
 
3941
            # the tip and tags data is cached.
 
3942
            return branch.Branch.heads_to_fetch(self)
 
3943
        medium = self._client._medium
 
3944
        if medium._is_remote_before((2, 4)):
 
3945
            return self._vfs_heads_to_fetch()
 
3946
        try:
 
3947
            return self._rpc_heads_to_fetch()
 
3948
        except errors.UnknownSmartMethod:
 
3949
            medium._remember_remote_is_before((2, 4))
 
3950
            return self._vfs_heads_to_fetch()
 
3951
 
 
3952
    def _rpc_heads_to_fetch(self):
 
3953
        response = self._call('Branch.heads_to_fetch', self._remote_path())
 
3954
        if len(response) != 2:
 
3955
            raise errors.UnexpectedSmartServerResponse(response)
 
3956
        must_fetch, if_present_fetch = response
 
3957
        return set(must_fetch), set(if_present_fetch)
 
3958
 
 
3959
    def _vfs_heads_to_fetch(self):
2715
3960
        self._ensure_real()
2716
 
        return self._real_branch.set_push_location(location)
 
3961
        return self._real_branch.heads_to_fetch()
2717
3962
 
2718
3963
 
2719
3964
class RemoteConfig(object):
2721
3966
 
2722
3967
    It is a low-level object that considers config data to be name/value pairs
2723
3968
    that may be associated with a section. Assigning meaning to the these
2724
 
    values is done at higher levels like bzrlib.config.TreeConfig.
 
3969
    values is done at higher levels like breezy.config.TreeConfig.
2725
3970
    """
2726
3971
 
2727
3972
    def get_option(self, name, section=None, default=None):
2734
3979
        """
2735
3980
        try:
2736
3981
            configobj = self._get_configobj()
 
3982
            section_obj = None
2737
3983
            if section is None:
2738
3984
                section_obj = configobj
2739
3985
            else:
2740
3986
                try:
2741
3987
                    section_obj = configobj[section]
2742
3988
                except KeyError:
2743
 
                    return default
2744
 
            return section_obj.get(name, default)
 
3989
                    pass
 
3990
            if section_obj is None:
 
3991
                value = default
 
3992
            else:
 
3993
                value = section_obj.get(name, default)
2745
3994
        except errors.UnknownSmartMethod:
2746
 
            return self._vfs_get_option(name, section, default)
 
3995
            value = self._vfs_get_option(name, section, default)
 
3996
        for hook in _mod_config.OldConfigHooks['get']:
 
3997
            hook(self, name, value)
 
3998
        return value
2747
3999
 
2748
4000
    def _response_to_configobj(self, response):
2749
4001
        if len(response[0]) and response[0][0] != 'ok':
2750
4002
            raise errors.UnexpectedSmartServerResponse(response)
2751
4003
        lines = response[1].read_body_bytes().splitlines()
2752
 
        return config.ConfigObj(lines, encoding='utf-8')
 
4004
        conf = _mod_config.ConfigObj(lines, encoding='utf-8')
 
4005
        for hook in _mod_config.OldConfigHooks['load']:
 
4006
            hook(self)
 
4007
        return conf
2753
4008
 
2754
4009
 
2755
4010
class RemoteBranchConfig(RemoteConfig):
2774
4029
        medium = self._branch._client._medium
2775
4030
        if medium._is_remote_before((1, 14)):
2776
4031
            return self._vfs_set_option(value, name, section)
 
4032
        if isinstance(value, dict):
 
4033
            if medium._is_remote_before((2, 2)):
 
4034
                return self._vfs_set_option(value, name, section)
 
4035
            return self._set_config_option_dict(value, name, section)
 
4036
        else:
 
4037
            return self._set_config_option(value, name, section)
 
4038
 
 
4039
    def _set_config_option(self, value, name, section):
2777
4040
        try:
2778
4041
            path = self._branch._remote_path()
2779
4042
            response = self._branch._client.call('Branch.set_config_option',
2780
4043
                path, self._branch._lock_token, self._branch._repo_lock_token,
2781
4044
                value.encode('utf8'), name, section or '')
2782
4045
        except errors.UnknownSmartMethod:
 
4046
            medium = self._branch._client._medium
2783
4047
            medium._remember_remote_is_before((1, 14))
2784
4048
            return self._vfs_set_option(value, name, section)
2785
4049
        if response != ():
2786
4050
            raise errors.UnexpectedSmartServerResponse(response)
2787
4051
 
 
4052
    def _serialize_option_dict(self, option_dict):
 
4053
        utf8_dict = {}
 
4054
        for key, value in option_dict.items():
 
4055
            if isinstance(key, unicode):
 
4056
                key = key.encode('utf8')
 
4057
            if isinstance(value, unicode):
 
4058
                value = value.encode('utf8')
 
4059
            utf8_dict[key] = value
 
4060
        return bencode.bencode(utf8_dict)
 
4061
 
 
4062
    def _set_config_option_dict(self, value, name, section):
 
4063
        try:
 
4064
            path = self._branch._remote_path()
 
4065
            serialised_dict = self._serialize_option_dict(value)
 
4066
            response = self._branch._client.call(
 
4067
                'Branch.set_config_option_dict',
 
4068
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
4069
                serialised_dict, name, section or '')
 
4070
        except errors.UnknownSmartMethod:
 
4071
            medium = self._branch._client._medium
 
4072
            medium._remember_remote_is_before((2, 2))
 
4073
            return self._vfs_set_option(value, name, section)
 
4074
        if response != ():
 
4075
            raise errors.UnexpectedSmartServerResponse(response)
 
4076
 
2788
4077
    def _real_object(self):
2789
4078
        self._branch._ensure_real()
2790
4079
        return self._branch._real_branch
2829
4118
        return self._bzrdir._real_bzrdir
2830
4119
 
2831
4120
 
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)
 
4121
error_translators = registry.Registry()
 
4122
no_context_error_translators = registry.Registry()
2840
4123
 
2841
4124
 
2842
4125
def _translate_error(err, **context):
2856
4139
    def find(name):
2857
4140
        try:
2858
4141
            return context[name]
2859
 
        except KeyError, key_err:
 
4142
        except KeyError as key_err:
2860
4143
            mutter('Missing key %r in context %r', key_err.args[0], context)
2861
4144
            raise err
2862
4145
    def get_path():
2865
4148
        """
2866
4149
        try:
2867
4150
            return context['path']
2868
 
        except KeyError, key_err:
 
4151
        except KeyError as key_err:
2869
4152
            try:
2870
4153
                return err.error_args[0]
2871
 
            except IndexError, idx_err:
 
4154
            except IndexError as idx_err:
2872
4155
                mutter(
2873
4156
                    'Missing key %r in context %r', key_err.args[0], context)
2874
4157
                raise err
2875
4158
 
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'):
 
4159
    try:
 
4160
        translator = error_translators.get(err.error_verb)
 
4161
    except KeyError:
 
4162
        pass
 
4163
    else:
 
4164
        raise translator(err, find, get_path)
 
4165
    try:
 
4166
        translator = no_context_error_translators.get(err.error_verb)
 
4167
    except KeyError:
 
4168
        raise errors.UnknownErrorFromSmartServer(err)
 
4169
    else:
 
4170
        raise translator(err)
 
4171
 
 
4172
 
 
4173
error_translators.register('NoSuchRevision',
 
4174
    lambda err, find, get_path: NoSuchRevision(
 
4175
        find('branch'), err.error_args[0]))
 
4176
error_translators.register('nosuchrevision',
 
4177
    lambda err, find, get_path: NoSuchRevision(
 
4178
        find('repository'), err.error_args[0]))
 
4179
 
 
4180
def _translate_nobranch_error(err, find, get_path):
 
4181
    if len(err.error_args) >= 1:
 
4182
        extra = err.error_args[0]
 
4183
    else:
 
4184
        extra = None
 
4185
    return errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
4186
        detail=extra)
 
4187
 
 
4188
error_translators.register('nobranch', _translate_nobranch_error)
 
4189
error_translators.register('norepository',
 
4190
    lambda err, find, get_path: errors.NoRepositoryPresent(
 
4191
        find('bzrdir')))
 
4192
error_translators.register('UnlockableTransport',
 
4193
    lambda err, find, get_path: errors.UnlockableTransport(
 
4194
        find('bzrdir').root_transport))
 
4195
error_translators.register('TokenMismatch',
 
4196
    lambda err, find, get_path: errors.TokenMismatch(
 
4197
        find('token'), '(remote token)'))
 
4198
error_translators.register('Diverged',
 
4199
    lambda err, find, get_path: errors.DivergedBranches(
 
4200
        find('branch'), find('other_branch')))
 
4201
error_translators.register('NotStacked',
 
4202
    lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
 
4203
 
 
4204
def _translate_PermissionDenied(err, find, get_path):
 
4205
    path = get_path()
 
4206
    if len(err.error_args) >= 2:
 
4207
        extra = err.error_args[1]
 
4208
    else:
 
4209
        extra = None
 
4210
    return errors.PermissionDenied(path, extra=extra)
 
4211
 
 
4212
error_translators.register('PermissionDenied', _translate_PermissionDenied)
 
4213
error_translators.register('ReadError',
 
4214
    lambda err, find, get_path: errors.ReadError(get_path()))
 
4215
error_translators.register('NoSuchFile',
 
4216
    lambda err, find, get_path: errors.NoSuchFile(get_path()))
 
4217
error_translators.register('TokenLockingNotSupported',
 
4218
    lambda err, find, get_path: errors.TokenLockingNotSupported(
 
4219
        find('repository')))
 
4220
error_translators.register('UnsuspendableWriteGroup',
 
4221
    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
 
4222
        repository=find('repository')))
 
4223
error_translators.register('UnresumableWriteGroup',
 
4224
    lambda err, find, get_path: errors.UnresumableWriteGroup(
 
4225
        repository=find('repository'), write_groups=err.error_args[0],
 
4226
        reason=err.error_args[1]))
 
4227
no_context_error_translators.register('IncompatibleRepositories',
 
4228
    lambda err: errors.IncompatibleRepositories(
 
4229
        err.error_args[0], err.error_args[1], err.error_args[2]))
 
4230
no_context_error_translators.register('LockContention',
 
4231
    lambda err: errors.LockContention('(remote lock)'))
 
4232
no_context_error_translators.register('LockFailed',
 
4233
    lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
 
4234
no_context_error_translators.register('TipChangeRejected',
 
4235
    lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
 
4236
no_context_error_translators.register('UnstackableBranchFormat',
 
4237
    lambda err: branch.UnstackableBranchFormat(*err.error_args))
 
4238
no_context_error_translators.register('UnstackableRepositoryFormat',
 
4239
    lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
 
4240
no_context_error_translators.register('FileExists',
 
4241
    lambda err: errors.FileExists(err.error_args[0]))
 
4242
no_context_error_translators.register('DirectoryNotEmpty',
 
4243
    lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
 
4244
 
 
4245
def _translate_short_readv_error(err):
 
4246
    args = err.error_args
 
4247
    return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
 
4248
        int(args[3]))
 
4249
 
 
4250
no_context_error_translators.register('ShortReadvError',
 
4251
    _translate_short_readv_error)
 
4252
 
 
4253
def _translate_unicode_error(err):
2932
4254
        encoding = str(err.error_args[0]) # encoding must always be a string
2933
4255
        val = err.error_args[1]
2934
4256
        start = int(err.error_args[2])
2942
4264
            raise UnicodeDecodeError(encoding, val, start, end, reason)
2943
4265
        elif err.error_verb == 'UnicodeEncodeError':
2944
4266
            raise UnicodeEncodeError(encoding, val, start, end, reason)
2945
 
    elif err.error_verb == 'ReadOnlyError':
2946
 
        raise errors.TransportNotPossible('readonly transport')
2947
 
    raise errors.UnknownErrorFromSmartServer(err)
 
4267
 
 
4268
no_context_error_translators.register('UnicodeEncodeError',
 
4269
    _translate_unicode_error)
 
4270
no_context_error_translators.register('UnicodeDecodeError',
 
4271
    _translate_unicode_error)
 
4272
no_context_error_translators.register('ReadOnlyError',
 
4273
    lambda err: errors.TransportNotPossible('readonly transport'))
 
4274
no_context_error_translators.register('MemoryError',
 
4275
    lambda err: errors.BzrError("remote server out of memory\n"
 
4276
        "Retry non-remotely, or contact the server admin for details."))
 
4277
no_context_error_translators.register('RevisionNotPresent',
 
4278
    lambda err: errors.RevisionNotPresent(err.error_args[0], err.error_args[1]))
 
4279
 
 
4280
no_context_error_translators.register('BzrCheckError',
 
4281
    lambda err: errors.BzrCheckError(msg=err.error_args[0]))
 
4282