~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Robert Collins
  • Date: 2010-05-06 07:48:22 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506074822-0bsgf2j4h8jx0xkk
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
our first in-tree matcher. See the module docstring for details.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
19
19
from bzrlib import (
20
20
    bencode,
21
21
    branch,
22
 
    bzrdir as _mod_bzrdir,
 
22
    bzrdir,
23
23
    config,
24
 
    controldir,
25
24
    debug,
26
25
    errors,
27
26
    graph,
28
27
    lock,
29
28
    lockdir,
 
29
    repository,
30
30
    repository as _mod_repository,
 
31
    revision,
31
32
    revision as _mod_revision,
32
33
    static_tuple,
33
34
    symbol_versioning,
34
 
    urlutils,
35
 
    vf_repository,
36
 
    )
37
 
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
35
)
 
36
from bzrlib.branch import BranchReferenceFormat
 
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
38
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
39
39
from bzrlib.errors import (
40
40
    NoSuchRevision,
41
41
    SmartProtocolError,
42
42
    )
43
 
from bzrlib.i18n import gettext
44
43
from bzrlib.lockable_files import LockableFiles
45
44
from bzrlib.smart import client, vfs, repository as smart_repo
46
 
from bzrlib.smart.client import _SmartClient
47
 
from bzrlib.revision import NULL_REVISION
48
 
from bzrlib.repository import RepositoryWriteLockResult, _LazyListJoin
 
45
from bzrlib.revision import ensure_null, NULL_REVISION
49
46
from bzrlib.trace import mutter, note, warning
50
47
 
51
48
 
52
 
_DEFAULT_SEARCH_DEPTH = 100
53
 
 
54
 
 
55
49
class _RpcHelper(object):
56
50
    """Mixin class that helps with issuing RPCs."""
57
51
 
92
86
    return format
93
87
 
94
88
 
95
 
# Note that RemoteBzrDirProber lives in bzrlib.bzrdir so bzrlib.remote
96
 
# does not have to be imported unless a remote format is involved.
97
 
 
98
 
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
99
 
    """Format representing bzrdirs accessed via a smart server"""
100
 
 
101
 
    supports_workingtrees = False
102
 
 
103
 
    def __init__(self):
104
 
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
105
 
        # XXX: It's a bit ugly that the network name is here, because we'd
106
 
        # like to believe that format objects are stateless or at least
107
 
        # immutable,  However, we do at least avoid mutating the name after
108
 
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
109
 
        self._network_name = None
110
 
 
111
 
    def __repr__(self):
112
 
        return "%s(_network_name=%r)" % (self.__class__.__name__,
113
 
            self._network_name)
114
 
 
115
 
    def get_format_description(self):
116
 
        if self._network_name:
117
 
            real_format = controldir.network_format_registry.get(self._network_name)
118
 
            return 'Remote: ' + real_format.get_format_description()
119
 
        return 'bzr remote bzrdir'
120
 
 
121
 
    def get_format_string(self):
122
 
        raise NotImplementedError(self.get_format_string)
123
 
 
124
 
    def network_name(self):
125
 
        if self._network_name:
126
 
            return self._network_name
127
 
        else:
128
 
            raise AssertionError("No network name set.")
129
 
 
130
 
    def initialize_on_transport(self, transport):
131
 
        try:
132
 
            # hand off the request to the smart server
133
 
            client_medium = transport.get_smart_medium()
134
 
        except errors.NoSmartMedium:
135
 
            # TODO: lookup the local format from a server hint.
136
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
137
 
            return local_dir_format.initialize_on_transport(transport)
138
 
        client = _SmartClient(client_medium)
139
 
        path = client.remote_path_from_transport(transport)
140
 
        try:
141
 
            response = client.call('BzrDirFormat.initialize', path)
142
 
        except errors.ErrorFromSmartServer, err:
143
 
            _translate_error(err, path=path)
144
 
        if response[0] != 'ok':
145
 
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
146
 
        format = RemoteBzrDirFormat()
147
 
        self._supply_sub_formats_to(format)
148
 
        return RemoteBzrDir(transport, format)
149
 
 
150
 
    def parse_NoneTrueFalse(self, arg):
151
 
        if not arg:
152
 
            return None
153
 
        if arg == 'False':
154
 
            return False
155
 
        if arg == 'True':
156
 
            return True
157
 
        raise AssertionError("invalid arg %r" % arg)
158
 
 
159
 
    def _serialize_NoneTrueFalse(self, arg):
160
 
        if arg is False:
161
 
            return 'False'
162
 
        if arg:
163
 
            return 'True'
164
 
        return ''
165
 
 
166
 
    def _serialize_NoneString(self, arg):
167
 
        return arg or ''
168
 
 
169
 
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
170
 
        create_prefix=False, force_new_repo=False, stacked_on=None,
171
 
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
172
 
        shared_repo=False):
173
 
        try:
174
 
            # hand off the request to the smart server
175
 
            client_medium = transport.get_smart_medium()
176
 
        except errors.NoSmartMedium:
177
 
            do_vfs = True
178
 
        else:
179
 
            # Decline to open it if the server doesn't support our required
180
 
            # version (3) so that the VFS-based transport will do it.
181
 
            if client_medium.should_probe():
182
 
                try:
183
 
                    server_version = client_medium.protocol_version()
184
 
                    if server_version != '2':
185
 
                        do_vfs = True
186
 
                    else:
187
 
                        do_vfs = False
188
 
                except errors.SmartProtocolError:
189
 
                    # Apparently there's no usable smart server there, even though
190
 
                    # the medium supports the smart protocol.
191
 
                    do_vfs = True
192
 
            else:
193
 
                do_vfs = False
194
 
        if not do_vfs:
195
 
            client = _SmartClient(client_medium)
196
 
            path = client.remote_path_from_transport(transport)
197
 
            if client_medium._is_remote_before((1, 16)):
198
 
                do_vfs = True
199
 
        if do_vfs:
200
 
            # TODO: lookup the local format from a server hint.
201
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
202
 
            self._supply_sub_formats_to(local_dir_format)
203
 
            return local_dir_format.initialize_on_transport_ex(transport,
204
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
205
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
206
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
207
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
208
 
                vfs_only=True)
209
 
        return self._initialize_on_transport_ex_rpc(client, path, transport,
210
 
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
211
 
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
212
 
 
213
 
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
214
 
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
215
 
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
216
 
        args = []
217
 
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
218
 
        args.append(self._serialize_NoneTrueFalse(create_prefix))
219
 
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
220
 
        args.append(self._serialize_NoneString(stacked_on))
221
 
        # stack_on_pwd is often/usually our transport
222
 
        if stack_on_pwd:
223
 
            try:
224
 
                stack_on_pwd = transport.relpath(stack_on_pwd)
225
 
                if not stack_on_pwd:
226
 
                    stack_on_pwd = '.'
227
 
            except errors.PathNotChild:
228
 
                pass
229
 
        args.append(self._serialize_NoneString(stack_on_pwd))
230
 
        args.append(self._serialize_NoneString(repo_format_name))
231
 
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
232
 
        args.append(self._serialize_NoneTrueFalse(shared_repo))
233
 
        request_network_name = self._network_name or \
234
 
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
235
 
        try:
236
 
            response = client.call('BzrDirFormat.initialize_ex_1.16',
237
 
                request_network_name, path, *args)
238
 
        except errors.UnknownSmartMethod:
239
 
            client._medium._remember_remote_is_before((1,16))
240
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
241
 
            self._supply_sub_formats_to(local_dir_format)
242
 
            return local_dir_format.initialize_on_transport_ex(transport,
243
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
244
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
245
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
246
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
247
 
                vfs_only=True)
248
 
        except errors.ErrorFromSmartServer, err:
249
 
            _translate_error(err, path=path)
250
 
        repo_path = response[0]
251
 
        bzrdir_name = response[6]
252
 
        require_stacking = response[7]
253
 
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
254
 
        format = RemoteBzrDirFormat()
255
 
        format._network_name = bzrdir_name
256
 
        self._supply_sub_formats_to(format)
257
 
        bzrdir = RemoteBzrDir(transport, format, _client=client)
258
 
        if repo_path:
259
 
            repo_format = response_tuple_to_repo_format(response[1:])
260
 
            if repo_path == '.':
261
 
                repo_path = ''
262
 
            if repo_path:
263
 
                repo_bzrdir_format = RemoteBzrDirFormat()
264
 
                repo_bzrdir_format._network_name = response[5]
265
 
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
266
 
                    repo_bzrdir_format)
267
 
            else:
268
 
                repo_bzr = bzrdir
269
 
            final_stack = response[8] or None
270
 
            final_stack_pwd = response[9] or None
271
 
            if final_stack_pwd:
272
 
                final_stack_pwd = urlutils.join(
273
 
                    transport.base, final_stack_pwd)
274
 
            remote_repo = RemoteRepository(repo_bzr, repo_format)
275
 
            if len(response) > 10:
276
 
                # Updated server verb that locks remotely.
277
 
                repo_lock_token = response[10] or None
278
 
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
279
 
                if repo_lock_token:
280
 
                    remote_repo.dont_leave_lock_in_place()
281
 
            else:
282
 
                remote_repo.lock_write()
283
 
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
284
 
                final_stack_pwd, require_stacking)
285
 
            policy.acquire_repository()
286
 
        else:
287
 
            remote_repo = None
288
 
            policy = None
289
 
        bzrdir._format.set_branch_format(self.get_branch_format())
290
 
        if require_stacking:
291
 
            # The repo has already been created, but we need to make sure that
292
 
            # we'll make a stackable branch.
293
 
            bzrdir._format.require_stacking(_skip_repo=True)
294
 
        return remote_repo, bzrdir, require_stacking, policy
295
 
 
296
 
    def _open(self, transport):
297
 
        return RemoteBzrDir(transport, self)
298
 
 
299
 
    def __eq__(self, other):
300
 
        if not isinstance(other, RemoteBzrDirFormat):
301
 
            return False
302
 
        return self.get_format_description() == other.get_format_description()
303
 
 
304
 
    def __return_repository_format(self):
305
 
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
306
 
        # repository format has been asked for, tell the RemoteRepositoryFormat
307
 
        # that it should use that for init() etc.
308
 
        result = RemoteRepositoryFormat()
309
 
        custom_format = getattr(self, '_repository_format', None)
310
 
        if custom_format:
311
 
            if isinstance(custom_format, RemoteRepositoryFormat):
312
 
                return custom_format
313
 
            else:
314
 
                # We will use the custom format to create repositories over the
315
 
                # wire; expose its details like rich_root_data for code to
316
 
                # query
317
 
                result._custom_format = custom_format
318
 
        return result
319
 
 
320
 
    def get_branch_format(self):
321
 
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
322
 
        if not isinstance(result, RemoteBranchFormat):
323
 
            new_result = RemoteBranchFormat()
324
 
            new_result._custom_format = result
325
 
            # cache the result
326
 
            self.set_branch_format(new_result)
327
 
            result = new_result
328
 
        return result
329
 
 
330
 
    repository_format = property(__return_repository_format,
331
 
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
332
 
 
333
 
 
334
 
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
 
89
# Note: RemoteBzrDirFormat is in bzrdir.py
 
90
 
 
91
class RemoteBzrDir(BzrDir, _RpcHelper):
335
92
    """Control directory on a remote server, accessed via bzr:// or similar."""
336
93
 
337
94
    def __init__(self, transport, format, _client=None, _force_probe=False):
340
97
        :param _client: Private parameter for testing. Disables probing and the
341
98
            use of a real bzrdir.
342
99
        """
343
 
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
 
100
        BzrDir.__init__(self, transport, format)
344
101
        # this object holds a delegated bzrdir that uses file-level operations
345
102
        # to talk to the other side
346
103
        self._real_bzrdir = None
406
163
                import traceback
407
164
                warning('VFS BzrDir access triggered\n%s',
408
165
                    ''.join(traceback.format_stack()))
409
 
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
 
166
            self._real_bzrdir = BzrDir.open_from_transport(
410
167
                self.root_transport, _server_formats=False)
411
168
            self._format._network_name = \
412
169
                self._real_bzrdir._format.network_name()
418
175
        # Prevent aliasing problems in the next_open_branch_result cache.
419
176
        # See create_branch for rationale.
420
177
        self._next_open_branch_result = None
421
 
        return _mod_bzrdir.BzrDir.break_lock(self)
 
178
        return BzrDir.break_lock(self)
422
179
 
423
180
    def _vfs_cloning_metadir(self, require_stacking=False):
424
181
        self._ensure_real()
455
212
        if len(branch_info) != 2:
456
213
            raise errors.UnexpectedSmartServerResponse(response)
457
214
        branch_ref, branch_name = branch_info
458
 
        format = controldir.network_format_registry.get(control_name)
 
215
        format = bzrdir.network_format_registry.get(control_name)
459
216
        if repo_name:
460
 
            format.repository_format = _mod_repository.network_format_registry.get(
 
217
            format.repository_format = repository.network_format_registry.get(
461
218
                repo_name)
462
219
        if branch_ref == 'ref':
463
220
            # XXX: we need possible_transports here to avoid reopening the
464
221
            # connection to the referenced location
465
 
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
 
222
            ref_bzrdir = BzrDir.open(branch_name)
466
223
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
467
224
            format.set_branch_format(branch_format)
468
225
        elif branch_ref == 'branch':
487
244
        self._ensure_real()
488
245
        self._real_bzrdir.destroy_repository()
489
246
 
490
 
    def create_branch(self, name=None, repository=None,
491
 
                      append_revisions_only=None):
 
247
    def create_branch(self, name=None):
492
248
        # as per meta1 formats - just delegate to the format object which may
493
249
        # be parameterised.
494
250
        real_branch = self._format.get_branch_format().initialize(self,
495
 
            name=name, repository=repository,
496
 
            append_revisions_only=append_revisions_only)
 
251
            name=name)
497
252
        if not isinstance(real_branch, RemoteBranch):
498
 
            if not isinstance(repository, RemoteRepository):
499
 
                raise AssertionError(
500
 
                    'need a RemoteRepository to use with RemoteBranch, got %r'
501
 
                    % (repository,))
502
 
            result = RemoteBranch(self, repository, real_branch, name=name)
 
253
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
254
                                  name=name)
503
255
        else:
504
256
            result = real_branch
505
257
        # BzrDir.clone_on_transport() uses the result of create_branch but does
517
269
        self._real_bzrdir.destroy_branch(name=name)
518
270
        self._next_open_branch_result = None
519
271
 
520
 
    def create_workingtree(self, revision_id=None, from_branch=None,
521
 
        accelerator_tree=None, hardlink=False):
 
272
    def create_workingtree(self, revision_id=None, from_branch=None):
522
273
        raise errors.NotLocalUrl(self.transport.base)
523
274
 
524
 
    def find_branch_format(self, name=None):
 
275
    def find_branch_format(self):
525
276
        """Find the branch 'format' for this bzrdir.
526
277
 
527
278
        This might be a synthetic object for e.g. RemoteBranch and SVN.
528
279
        """
529
 
        b = self.open_branch(name=name)
 
280
        b = self.open_branch()
530
281
        return b._format
531
282
 
532
 
    def get_branch_reference(self, name=None):
 
283
    def get_branch_reference(self):
533
284
        """See BzrDir.get_branch_reference()."""
534
 
        if name is not None:
535
 
            # XXX JRV20100304: Support opening colocated branches
536
 
            raise errors.NoColocatedBranchSupport(self)
537
285
        response = self._get_branch_reference()
538
286
        if response[0] == 'ref':
539
287
            return response[1]
570
318
            raise errors.UnexpectedSmartServerResponse(response)
571
319
        return response
572
320
 
573
 
    def _get_tree_branch(self, name=None):
 
321
    def _get_tree_branch(self):
574
322
        """See BzrDir._get_tree_branch()."""
575
 
        return None, self.open_branch(name=name)
 
323
        return None, self.open_branch()
576
324
 
577
325
    def open_branch(self, name=None, unsupported=False,
578
326
                    ignore_fallbacks=False):
675
423
 
676
424
    def _path_for_remote_call(self, client):
677
425
        """Return the path to be used for this bzrdir in a remote call."""
678
 
        return urlutils.split_segment_parameters_raw(
679
 
            client.remote_path_from_transport(self.root_transport))[0]
 
426
        return client.remote_path_from_transport(self.root_transport)
680
427
 
681
428
    def get_branch_transport(self, branch_format, name=None):
682
429
        self._ensure_real()
694
441
        """Upgrading of remote bzrdirs is not supported yet."""
695
442
        return False
696
443
 
697
 
    def needs_format_conversion(self, format):
 
444
    def needs_format_conversion(self, format=None):
698
445
        """Upgrading of remote bzrdirs is not supported yet."""
 
446
        if format is None:
 
447
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
 
448
                % 'needs_format_conversion(format=None)')
699
449
        return False
700
450
 
701
451
    def clone(self, url, revision_id=None, force_new_repo=False,
708
458
        return RemoteBzrDirConfig(self)
709
459
 
710
460
 
711
 
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
 
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
712
462
    """Format for repositories accessed over a _SmartClient.
713
463
 
714
464
    Instances of this repository are represented by RemoteRepository
729
479
    """
730
480
 
731
481
    _matchingbzrdir = RemoteBzrDirFormat()
732
 
    supports_full_versioned_files = True
733
 
    supports_leaving_lock = True
734
482
 
735
483
    def __init__(self):
736
 
        _mod_repository.RepositoryFormat.__init__(self)
 
484
        repository.RepositoryFormat.__init__(self)
737
485
        self._custom_format = None
738
486
        self._network_name = None
739
487
        self._creating_bzrdir = None
740
 
        self._revision_graph_can_have_wrong_parents = None
741
488
        self._supports_chks = None
742
489
        self._supports_external_lookups = None
743
490
        self._supports_tree_reference = None
744
 
        self._supports_funky_characters = None
745
 
        self._supports_nesting_repositories = None
746
491
        self._rich_root_data = None
747
492
 
748
493
    def __repr__(self):
777
522
        return self._supports_external_lookups
778
523
 
779
524
    @property
780
 
    def supports_funky_characters(self):
781
 
        if self._supports_funky_characters is None:
782
 
            self._ensure_real()
783
 
            self._supports_funky_characters = \
784
 
                self._custom_format.supports_funky_characters
785
 
        return self._supports_funky_characters
786
 
 
787
 
    @property
788
 
    def supports_nesting_repositories(self):
789
 
        if self._supports_nesting_repositories is None:
790
 
            self._ensure_real()
791
 
            self._supports_nesting_repositories = \
792
 
                self._custom_format.supports_nesting_repositories
793
 
        return self._supports_nesting_repositories
794
 
 
795
 
    @property
796
525
    def supports_tree_reference(self):
797
526
        if self._supports_tree_reference is None:
798
527
            self._ensure_real()
800
529
                self._custom_format.supports_tree_reference
801
530
        return self._supports_tree_reference
802
531
 
803
 
    @property
804
 
    def revision_graph_can_have_wrong_parents(self):
805
 
        if self._revision_graph_can_have_wrong_parents is None:
806
 
            self._ensure_real()
807
 
            self._revision_graph_can_have_wrong_parents = \
808
 
                self._custom_format.revision_graph_can_have_wrong_parents
809
 
        return self._revision_graph_can_have_wrong_parents
810
 
 
811
532
    def _vfs_initialize(self, a_bzrdir, shared):
812
533
        """Helper for common code in initialize."""
813
534
        if self._custom_format:
848
569
            network_name = self._network_name
849
570
        else:
850
571
            # Select the current bzrlib default and ask for that.
851
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
572
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
852
573
            reference_format = reference_bzrdir_format.repository_format
853
574
            network_name = reference_format.network_name()
854
575
        # 2) try direct creation via RPC
880
601
 
881
602
    def _ensure_real(self):
882
603
        if self._custom_format is None:
883
 
            self._custom_format = _mod_repository.network_format_registry.get(
 
604
            self._custom_format = repository.network_format_registry.get(
884
605
                self._network_name)
885
606
 
886
607
    @property
923
644
 
924
645
 
925
646
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
926
 
    controldir.ControlComponent):
 
647
    bzrdir.ControlComponent):
927
648
    """Repository accessed over rpc.
928
649
 
929
650
    For the moment most operations are performed using local transport-backed
982
703
        # transport, but I'm not sure it's worth making this method
983
704
        # optional -- mbp 2010-04-21
984
705
        return self.bzrdir.get_repository_transport(None)
985
 
 
 
706
        
986
707
    def __str__(self):
987
708
        return "%s(%s)" % (self.__class__.__name__, self.base)
988
709
 
1096
817
    def find_text_key_references(self):
1097
818
        """Find the text key references within the repository.
1098
819
 
 
820
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
 
821
        revision_ids. Each altered file-ids has the exact revision_ids that
 
822
        altered it listed explicitly.
1099
823
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
1100
824
            to whether they were referred to by the inventory of the
1101
825
            revision_id that they contain. The inventory texts from all present
1119
843
        """Private method for using with old (< 1.2) servers to fallback."""
1120
844
        if revision_id is None:
1121
845
            revision_id = ''
1122
 
        elif _mod_revision.is_null(revision_id):
 
846
        elif revision.is_null(revision_id):
1123
847
            return {}
1124
848
 
1125
849
        path = self.bzrdir._path_for_remote_call(self._client)
1149
873
        return RemoteStreamSource(self, to_format)
1150
874
 
1151
875
    @needs_read_lock
1152
 
    def get_file_graph(self):
1153
 
        return graph.Graph(self.texts)
1154
 
 
1155
 
    @needs_read_lock
1156
876
    def has_revision(self, revision_id):
1157
877
        """True if this repository has a copy of the revision."""
1158
878
        # Copy of bzrlib.repository.Repository.has_revision
1175
895
    def _has_same_fallbacks(self, other_repo):
1176
896
        """Returns true if the repositories have the same fallbacks."""
1177
897
        # XXX: copied from Repository; it should be unified into a base class
1178
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
898
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
1179
899
        my_fb = self._fallback_repositories
1180
900
        other_fb = other_repo._fallback_repositories
1181
901
        if len(my_fb) != len(other_fb):
1210
930
        """See Repository.gather_stats()."""
1211
931
        path = self.bzrdir._path_for_remote_call(self._client)
1212
932
        # revid can be None to indicate no revisions, not just NULL_REVISION
1213
 
        if revid is None or _mod_revision.is_null(revid):
 
933
        if revid is None or revision.is_null(revid):
1214
934
            fmt_revid = ''
1215
935
        else:
1216
936
            fmt_revid = revid
1277
997
        pass
1278
998
 
1279
999
    def lock_read(self):
1280
 
        """Lock the repository for read operations.
1281
 
 
1282
 
        :return: A bzrlib.lock.LogicalLockResult.
1283
 
        """
1284
1000
        # wrong eventually - want a local lock cache context
1285
1001
        if not self._lock_mode:
1286
1002
            self._note_lock('r')
1293
1009
                repo.lock_read()
1294
1010
        else:
1295
1011
            self._lock_count += 1
1296
 
        return lock.LogicalLockResult(self.unlock)
1297
1012
 
1298
1013
    def _remote_lock_write(self, token):
1299
1014
        path = self.bzrdir._path_for_remote_call(self._client)
1339
1054
            raise errors.ReadOnlyError(self)
1340
1055
        else:
1341
1056
            self._lock_count += 1
1342
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
1057
        return self._lock_token or None
1343
1058
 
1344
1059
    def leave_lock_in_place(self):
1345
1060
        if not self._lock_token:
1499
1214
 
1500
1215
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1501
1216
                           timezone=None, committer=None, revprops=None,
1502
 
                           revision_id=None, lossy=False):
 
1217
                           revision_id=None):
1503
1218
        # FIXME: It ought to be possible to call this without immediately
1504
1219
        # triggering _ensure_real.  For now it's the easiest thing to do.
1505
1220
        self._ensure_real()
1506
1221
        real_repo = self._real_repository
1507
1222
        builder = real_repo.get_commit_builder(branch, parents,
1508
1223
                config, timestamp=timestamp, timezone=timezone,
1509
 
                committer=committer, revprops=revprops,
1510
 
                revision_id=revision_id, lossy=lossy)
 
1224
                committer=committer, revprops=revprops, revision_id=revision_id)
1511
1225
        return builder
1512
1226
 
1513
1227
    def add_fallback_repository(self, repository):
1521
1235
        # We need to accumulate additional repositories here, to pass them in
1522
1236
        # on various RPC's.
1523
1237
        #
1524
 
        # Make the check before we lock: this raises an exception.
1525
 
        self._check_fallback_repository(repository)
1526
1238
        if self.is_locked():
1527
1239
            # We will call fallback.unlock() when we transition to the unlocked
1528
1240
            # state, so always add a lock here. If a caller passes us a locked
1529
1241
            # repository, they are responsible for unlocking it later.
1530
1242
            repository.lock_read()
 
1243
        self._check_fallback_repository(repository)
1531
1244
        self._fallback_repositories.append(repository)
1532
1245
        # If self._real_repository was parameterised already (e.g. because a
1533
1246
        # _real_branch had its get_stacked_on_url method called), then the
1593
1306
        return self._real_repository.make_working_trees()
1594
1307
 
1595
1308
    def refresh_data(self):
1596
 
        """Re-read any data needed to synchronise with disk.
 
1309
        """Re-read any data needed to to synchronise with disk.
1597
1310
 
1598
1311
        This method is intended to be called after another repository instance
1599
1312
        (such as one used by a smart server) has inserted data into the
1600
 
        repository. On all repositories this will work outside of write groups.
1601
 
        Some repository formats (pack and newer for bzrlib native formats)
1602
 
        support refresh_data inside write groups. If called inside a write
1603
 
        group on a repository that does not support refreshing in a write group
1604
 
        IsInWriteGroupError will be raised.
 
1313
        repository. It may not be called during a write group, but may be
 
1314
        called at any other time.
1605
1315
        """
 
1316
        if self.is_in_write_group():
 
1317
            raise errors.InternalBzrError(
 
1318
                "May not refresh_data while in a write group.")
1606
1319
        if self._real_repository is not None:
1607
1320
            self._real_repository.refresh_data()
1608
1321
 
1620
1333
        return result
1621
1334
 
1622
1335
    @needs_read_lock
1623
 
    def search_missing_revision_ids(self, other,
1624
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
1625
 
            find_ghosts=True, revision_ids=None, if_present_ids=None,
1626
 
            limit=None):
 
1336
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1627
1337
        """Return the revision ids that other has that this does not.
1628
1338
 
1629
1339
        These are returned in topological order.
1630
1340
 
1631
1341
        revision_id: only return revision ids included by revision_id.
1632
1342
        """
1633
 
        if symbol_versioning.deprecated_passed(revision_id):
1634
 
            symbol_versioning.warn(
1635
 
                'search_missing_revision_ids(revision_id=...) was '
1636
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
1637
 
                DeprecationWarning, stacklevel=2)
1638
 
            if revision_ids is not None:
1639
 
                raise AssertionError(
1640
 
                    'revision_ids is mutually exclusive with revision_id')
1641
 
            if revision_id is not None:
1642
 
                revision_ids = [revision_id]
1643
 
        inter_repo = _mod_repository.InterRepository.get(other, self)
1644
 
        return inter_repo.search_missing_revision_ids(
1645
 
            find_ghosts=find_ghosts, revision_ids=revision_ids,
1646
 
            if_present_ids=if_present_ids, limit=limit)
 
1343
        return repository.InterRepository.get(
 
1344
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
1647
1345
 
1648
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1346
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
1649
1347
            fetch_spec=None):
1650
1348
        # No base implementation to use as RemoteRepository is not a subclass
1651
1349
        # of Repository; so this is a copy of Repository.fetch().
1662
1360
            # check that last_revision is in 'from' and then return a
1663
1361
            # no-operation.
1664
1362
            if (revision_id is not None and
1665
 
                not _mod_revision.is_null(revision_id)):
 
1363
                not revision.is_null(revision_id)):
1666
1364
                self.get_revision(revision_id)
1667
1365
            return 0, []
1668
1366
        # if there is no specific appropriate InterRepository, this will get
1669
1367
        # the InterRepository base class, which raises an
1670
1368
        # IncompatibleRepositories when asked to fetch.
1671
 
        inter = _mod_repository.InterRepository.get(source, self)
1672
 
        return inter.fetch(revision_id=revision_id,
 
1369
        inter = repository.InterRepository.get(source, self)
 
1370
        return inter.fetch(revision_id=revision_id, pb=pb,
1673
1371
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1674
1372
 
1675
1373
    def create_bundle(self, target, base, fileobj, format=None):
1677
1375
        self._real_repository.create_bundle(target, base, fileobj, format)
1678
1376
 
1679
1377
    @needs_read_lock
1680
 
    @symbol_versioning.deprecated_method(
1681
 
        symbol_versioning.deprecated_in((2, 4, 0)))
1682
1378
    def get_ancestry(self, revision_id, topo_sorted=True):
1683
1379
        self._ensure_real()
1684
1380
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
1698
1394
        self._ensure_real()
1699
1395
        return self._real_repository.iter_files_bytes(desired_files)
1700
1396
 
1701
 
    def get_cached_parent_map(self, revision_ids):
1702
 
        """See bzrlib.CachingParentsProvider.get_cached_parent_map"""
1703
 
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
1704
 
 
1705
1397
    def get_parent_map(self, revision_ids):
1706
1398
        """See bzrlib.Graph.get_parent_map()."""
1707
1399
        return self._make_parents_provider().get_parent_map(revision_ids)
1765
1457
        if parents_map is None:
1766
1458
            # Repository is not locked, so there's no cache.
1767
1459
            parents_map = {}
1768
 
        if _DEFAULT_SEARCH_DEPTH <= 0:
1769
 
            (start_set, stop_keys,
1770
 
             key_count) = graph.search_result_from_parent_map(
1771
 
                parents_map, self._unstacked_provider.missing_keys)
1772
 
        else:
1773
 
            (start_set, stop_keys,
1774
 
             key_count) = graph.limited_search_result_from_parent_map(
1775
 
                parents_map, self._unstacked_provider.missing_keys,
1776
 
                keys, depth=_DEFAULT_SEARCH_DEPTH)
 
1460
        # start_set is all the keys in the cache
 
1461
        start_set = set(parents_map)
 
1462
        # result set is all the references to keys in the cache
 
1463
        result_parents = set()
 
1464
        for parents in parents_map.itervalues():
 
1465
            result_parents.update(parents)
 
1466
        stop_keys = result_parents.difference(start_set)
 
1467
        # We don't need to send ghosts back to the server as a position to
 
1468
        # stop either.
 
1469
        stop_keys.difference_update(self._unstacked_provider.missing_keys)
 
1470
        key_count = len(parents_map)
 
1471
        if (NULL_REVISION in result_parents
 
1472
            and NULL_REVISION in self._unstacked_provider.missing_keys):
 
1473
            # If we pruned NULL_REVISION from the stop_keys because it's also
 
1474
            # in our cache of "missing" keys we need to increment our key count
 
1475
            # by 1, because the reconsitituted SearchResult on the server will
 
1476
            # still consider NULL_REVISION to be an included key.
 
1477
            key_count += 1
 
1478
        included_keys = start_set.intersection(result_parents)
 
1479
        start_set.difference_update(included_keys)
1777
1480
        recipe = ('manual', start_set, stop_keys, key_count)
1778
1481
        body = self._serialise_search_recipe(recipe)
1779
1482
        path = self.bzrdir._path_for_remote_call(self._client)
1883
1586
        from bzrlib import osutils
1884
1587
        import tarfile
1885
1588
        # TODO: Maybe a progress bar while streaming the tarball?
1886
 
        note(gettext("Copying repository content as tarball..."))
 
1589
        note("Copying repository content as tarball...")
1887
1590
        tar_file = self._get_tarball('bz2')
1888
1591
        if tar_file is None:
1889
1592
            return None
1894
1597
            tmpdir = osutils.mkdtemp()
1895
1598
            try:
1896
1599
                _extract_tar(tar, tmpdir)
1897
 
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
 
1600
                tmp_bzrdir = BzrDir.open(tmpdir)
1898
1601
                tmp_repo = tmp_bzrdir.open_repository()
1899
1602
                tmp_repo.copy_content_into(destination, revision_id)
1900
1603
            finally:
1985
1688
    def supports_rich_root(self):
1986
1689
        return self._format.rich_root_data
1987
1690
 
1988
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1989
1691
    def iter_reverse_revision_history(self, revision_id):
1990
1692
        self._ensure_real()
1991
1693
        return self._real_repository.iter_reverse_revision_history(revision_id)
2012
1714
        return self._real_repository.item_keys_introduced_by(revision_ids,
2013
1715
            _files_pb=_files_pb)
2014
1716
 
 
1717
    def revision_graph_can_have_wrong_parents(self):
 
1718
        # The answer depends on the remote repo format.
 
1719
        self._ensure_real()
 
1720
        return self._real_repository.revision_graph_can_have_wrong_parents()
 
1721
 
2015
1722
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2016
1723
        self._ensure_real()
2017
1724
        return self._real_repository._find_inconsistent_revision_parents(
2025
1732
        providers = [self._unstacked_provider]
2026
1733
        if other is not None:
2027
1734
            providers.insert(0, other)
2028
 
        return graph.StackedParentsProvider(_LazyListJoin(
2029
 
            providers, self._fallback_repositories))
 
1735
        providers.extend(r._make_parents_provider() for r in
 
1736
                         self._fallback_repositories)
 
1737
        return graph.StackedParentsProvider(providers)
2030
1738
 
2031
1739
    def _serialise_search_recipe(self, recipe):
2032
1740
        """Serialise a graph search recipe.
2040
1748
        return '\n'.join((start_keys, stop_keys, count))
2041
1749
 
2042
1750
    def _serialise_search_result(self, search_result):
2043
 
        parts = search_result.get_network_struct()
 
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)]
2044
1757
        return '\n'.join(parts)
2045
1758
 
2046
1759
    def autopack(self):
2056
1769
            raise errors.UnexpectedSmartServerResponse(response)
2057
1770
 
2058
1771
 
2059
 
class RemoteStreamSink(vf_repository.StreamSink):
 
1772
class RemoteStreamSink(repository.StreamSink):
2060
1773
 
2061
1774
    def _insert_real(self, stream, src_format, resume_tokens):
2062
1775
        self.target_repo._ensure_real()
2163
1876
        self._last_substream and self._last_stream so that the stream can be
2164
1877
        resumed by _resume_stream_with_vfs.
2165
1878
        """
2166
 
 
 
1879
                    
2167
1880
        stream_iter = iter(stream)
2168
1881
        for substream_kind, substream in stream_iter:
2169
1882
            if substream_kind == 'inventory-deltas':
2172
1885
                return
2173
1886
            else:
2174
1887
                yield substream_kind, substream
2175
 
 
2176
 
 
2177
 
class RemoteStreamSource(vf_repository.StreamSource):
 
1888
            
 
1889
 
 
1890
class RemoteStreamSource(repository.StreamSource):
2178
1891
    """Stream data from a remote server."""
2179
1892
 
2180
1893
    def get_stream(self, search):
2240
1953
        candidate_verbs = [
2241
1954
            ('Repository.get_stream_1.19', (1, 19)),
2242
1955
            ('Repository.get_stream', (1, 13))]
2243
 
 
2244
1956
        found_verb = False
2245
1957
        for verb, version in candidate_verbs:
2246
1958
            if medium._is_remote_before(version):
2250
1962
                    verb, args, search_bytes)
2251
1963
            except errors.UnknownSmartMethod:
2252
1964
                medium._remember_remote_is_before(version)
2253
 
            except errors.UnknownErrorFromSmartServer, e:
2254
 
                if isinstance(search, graph.EverythingResult):
2255
 
                    error_verb = e.error_from_smart_server.error_verb
2256
 
                    if error_verb == 'BadSearch':
2257
 
                        # Pre-2.4 servers don't support this sort of search.
2258
 
                        # XXX: perhaps falling back to VFS on BadSearch is a
2259
 
                        # good idea in general?  It might provide a little bit
2260
 
                        # of protection against client-side bugs.
2261
 
                        medium._remember_remote_is_before((2, 4))
2262
 
                        break
2263
 
                raise
2264
1965
            else:
2265
1966
                response_tuple, response_handler = response
2266
1967
                found_verb = True
2270
1971
        if response_tuple[0] != 'ok':
2271
1972
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2272
1973
        byte_stream = response_handler.read_streamed_body()
2273
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
2274
 
            self._record_counter)
 
1974
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
2275
1975
        if src_format.network_name() != repo._format.network_name():
2276
1976
            raise AssertionError(
2277
1977
                "Mismatched RemoteRepository and stream src %r, %r" % (
2363
2063
        return a_bzrdir.open_branch(name=name, 
2364
2064
            ignore_fallbacks=ignore_fallbacks)
2365
2065
 
2366
 
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
 
2066
    def _vfs_initialize(self, a_bzrdir, name):
2367
2067
        # Initialisation when using a local bzrdir object, or a non-vfs init
2368
2068
        # method is not available on the server.
2369
2069
        # self._custom_format is always set - the start of initialize ensures
2371
2071
        if isinstance(a_bzrdir, RemoteBzrDir):
2372
2072
            a_bzrdir._ensure_real()
2373
2073
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2374
 
                name, append_revisions_only=append_revisions_only)
 
2074
                name)
2375
2075
        else:
2376
2076
            # We assume the bzrdir is parameterised; it may not be.
2377
 
            result = self._custom_format.initialize(a_bzrdir, name,
2378
 
                append_revisions_only=append_revisions_only)
 
2077
            result = self._custom_format.initialize(a_bzrdir, name)
2379
2078
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2380
2079
            not isinstance(result, RemoteBranch)):
2381
2080
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2382
2081
                                  name=name)
2383
2082
        return result
2384
2083
 
2385
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2386
 
                   append_revisions_only=None):
 
2084
    def initialize(self, a_bzrdir, name=None):
2387
2085
        # 1) get the network name to use.
2388
2086
        if self._custom_format:
2389
2087
            network_name = self._custom_format.network_name()
2390
2088
        else:
2391
2089
            # Select the current bzrlib default and ask for that.
2392
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
2090
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
2393
2091
            reference_format = reference_bzrdir_format.get_branch_format()
2394
2092
            self._custom_format = reference_format
2395
2093
            network_name = reference_format.network_name()
2396
2094
        # Being asked to create on a non RemoteBzrDir:
2397
2095
        if not isinstance(a_bzrdir, RemoteBzrDir):
2398
 
            return self._vfs_initialize(a_bzrdir, name=name,
2399
 
                append_revisions_only=append_revisions_only)
 
2096
            return self._vfs_initialize(a_bzrdir, name=name)
2400
2097
        medium = a_bzrdir._client._medium
2401
2098
        if medium._is_remote_before((1, 13)):
2402
 
            return self._vfs_initialize(a_bzrdir, name=name,
2403
 
                append_revisions_only=append_revisions_only)
 
2099
            return self._vfs_initialize(a_bzrdir, name=name)
2404
2100
        # Creating on a remote bzr dir.
2405
2101
        # 2) try direct creation via RPC
2406
2102
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2413
2109
        except errors.UnknownSmartMethod:
2414
2110
            # Fallback - use vfs methods
2415
2111
            medium._remember_remote_is_before((1, 13))
2416
 
            return self._vfs_initialize(a_bzrdir, name=name,
2417
 
                    append_revisions_only=append_revisions_only)
 
2112
            return self._vfs_initialize(a_bzrdir, name=name)
2418
2113
        if response[0] != 'ok':
2419
2114
            raise errors.UnexpectedSmartServerResponse(response)
2420
2115
        # Turn the response into a RemoteRepository object.
2421
2116
        format = RemoteBranchFormat(network_name=response[1])
2422
2117
        repo_format = response_tuple_to_repo_format(response[3:])
2423
 
        repo_path = response[2]
2424
 
        if repository is not None:
2425
 
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
2426
 
            url_diff = urlutils.relative_url(repository.user_url,
2427
 
                    remote_repo_url)
2428
 
            if url_diff != '.':
2429
 
                raise AssertionError(
2430
 
                    'repository.user_url %r does not match URL from server '
2431
 
                    'response (%r + %r)'
2432
 
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
2433
 
            remote_repo = repository
 
2118
        if response[2] == '':
 
2119
            repo_bzrdir = a_bzrdir
2434
2120
        else:
2435
 
            if repo_path == '':
2436
 
                repo_bzrdir = a_bzrdir
2437
 
            else:
2438
 
                repo_bzrdir = RemoteBzrDir(
2439
 
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
2440
 
                    a_bzrdir._client)
2441
 
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
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)
2442
2125
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2443
2126
            format=format, setup_stacking=False, name=name)
2444
 
        if append_revisions_only:
2445
 
            remote_branch.set_append_revisions_only(append_revisions_only)
2446
2127
        # XXX: We know this is a new branch, so it must have revno 0, revid
2447
2128
        # NULL_REVISION. Creating the branch locked would make this be unable
2448
2129
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2467
2148
        self._ensure_real()
2468
2149
        return self._custom_format.supports_set_append_revisions_only()
2469
2150
 
2470
 
    def _use_default_local_heads_to_fetch(self):
2471
 
        # If the branch format is a metadir format *and* its heads_to_fetch
2472
 
        # implementation is not overridden vs the base class, we can use the
2473
 
        # base class logic rather than use the heads_to_fetch RPC.  This is
2474
 
        # usually cheaper in terms of net round trips, as the last-revision and
2475
 
        # tags info fetched is cached and would be fetched anyway.
2476
 
        self._ensure_real()
2477
 
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
2478
 
            branch_class = self._custom_format._branch_class()
2479
 
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
2480
 
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
2481
 
                return True
2482
 
        return False
2483
2151
 
2484
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2485
2153
    """Branch stored on a server accessed by HPSS RPC.
2643
2311
                self.bzrdir, self._client)
2644
2312
        return self._control_files
2645
2313
 
2646
 
    def _get_checkout_format(self, lightweight=False):
 
2314
    def _get_checkout_format(self):
2647
2315
        self._ensure_real()
2648
 
        if lightweight:
2649
 
            format = RemoteBzrDirFormat()
2650
 
            self.bzrdir._format._supply_sub_formats_to(format)
2651
 
            format.workingtree_format = self._real_branch._get_checkout_format(
2652
 
                lightweight=lightweight).workingtree_format
2653
 
            return format
2654
 
        else:
2655
 
            return self._real_branch._get_checkout_format(lightweight=False)
 
2316
        return self._real_branch._get_checkout_format()
2656
2317
 
2657
2318
    def get_physical_lock_status(self):
2658
2319
        """See Branch.get_physical_lock_status()."""
2691
2352
            self._is_stacked = False
2692
2353
        else:
2693
2354
            self._is_stacked = True
2694
 
 
 
2355
        
2695
2356
    def _vfs_get_tags_bytes(self):
2696
2357
        self._ensure_real()
2697
2358
        return self._real_branch._get_tags_bytes()
2698
2359
 
2699
 
    @needs_read_lock
2700
2360
    def _get_tags_bytes(self):
2701
 
        if self._tags_bytes is None:
2702
 
            self._tags_bytes = self._get_tags_bytes_via_hpss()
2703
 
        return self._tags_bytes
2704
 
 
2705
 
    def _get_tags_bytes_via_hpss(self):
2706
2361
        medium = self._client._medium
2707
2362
        if medium._is_remote_before((1, 13)):
2708
2363
            return self._vfs_get_tags_bytes()
2718
2373
        return self._real_branch._set_tags_bytes(bytes)
2719
2374
 
2720
2375
    def _set_tags_bytes(self, bytes):
2721
 
        if self.is_locked():
2722
 
            self._tags_bytes = bytes
2723
2376
        medium = self._client._medium
2724
2377
        if medium._is_remote_before((1, 18)):
2725
2378
            self._vfs_set_tags_bytes(bytes)
2734
2387
            self._vfs_set_tags_bytes(bytes)
2735
2388
 
2736
2389
    def lock_read(self):
2737
 
        """Lock the branch for read operations.
2738
 
 
2739
 
        :return: A bzrlib.lock.LogicalLockResult.
2740
 
        """
2741
2390
        self.repository.lock_read()
2742
2391
        if not self._lock_mode:
2743
2392
            self._note_lock('r')
2747
2396
                self._real_branch.lock_read()
2748
2397
        else:
2749
2398
            self._lock_count += 1
2750
 
        return lock.LogicalLockResult(self.unlock)
2751
2399
 
2752
2400
    def _remote_lock_write(self, token):
2753
2401
        if token is None:
2754
2402
            branch_token = repo_token = ''
2755
2403
        else:
2756
2404
            branch_token = token
2757
 
            repo_token = self.repository.lock_write().repository_token
 
2405
            repo_token = self.repository.lock_write()
2758
2406
            self.repository.unlock()
2759
2407
        err_context = {'token': token}
2760
 
        try:
2761
 
            response = self._call(
2762
 
                'Branch.lock_write', self._remote_path(), branch_token,
2763
 
                repo_token or '', **err_context)
2764
 
        except errors.LockContention, e:
2765
 
            # The LockContention from the server doesn't have any
2766
 
            # information about the lock_url. We re-raise LockContention
2767
 
            # with valid lock_url.
2768
 
            raise errors.LockContention('(remote lock)',
2769
 
                self.repository.base.split('.bzr/')[0])
 
2408
        response = self._call(
 
2409
            'Branch.lock_write', self._remote_path(), branch_token,
 
2410
            repo_token or '', **err_context)
2770
2411
        if response[0] != 'ok':
2771
2412
            raise errors.UnexpectedSmartServerResponse(response)
2772
2413
        ok, branch_token, repo_token = response
2793
2434
            self._lock_mode = 'w'
2794
2435
            self._lock_count = 1
2795
2436
        elif self._lock_mode == 'r':
2796
 
            raise errors.ReadOnlyError(self)
 
2437
            raise errors.ReadOnlyTransaction
2797
2438
        else:
2798
2439
            if token is not None:
2799
2440
                # A token was given to lock_write, and we're relocking, so
2804
2445
            self._lock_count += 1
2805
2446
            # Re-lock the repository too.
2806
2447
            self.repository.lock_write(self._repo_lock_token)
2807
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2448
        return self._lock_token or None
2808
2449
 
2809
2450
    def _unlock(self, branch_token, repo_token):
2810
2451
        err_context = {'token': str((branch_token, repo_token))}
2879
2520
            missing_parent = parent_map[missing_parent]
2880
2521
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2881
2522
 
2882
 
    def _read_last_revision_info(self):
 
2523
    def _last_revision_info(self):
2883
2524
        response = self._call('Branch.last_revision_info', self._remote_path())
2884
2525
        if response[0] != 'ok':
2885
2526
            raise SmartProtocolError('unexpected response code %s' % (response,))
2948
2589
            raise errors.UnexpectedSmartServerResponse(response)
2949
2590
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2950
2591
 
2951
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2952
2592
    @needs_write_lock
2953
2593
    def set_revision_history(self, rev_history):
2954
 
        """See Branch.set_revision_history."""
2955
 
        self._set_revision_history(rev_history)
2956
 
 
2957
 
    @needs_write_lock
2958
 
    def _set_revision_history(self, rev_history):
2959
2594
        # Send just the tip revision of the history; the server will generate
2960
2595
        # the full history from that.  If the revision doesn't exist in this
2961
2596
        # branch, NoSuchRevision will be raised.
3019
2654
            _override_hook_target=self, **kwargs)
3020
2655
 
3021
2656
    @needs_read_lock
3022
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
 
2657
    def push(self, target, overwrite=False, stop_revision=None):
3023
2658
        self._ensure_real()
3024
2659
        return self._real_branch.push(
3025
 
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
 
2660
            target, overwrite=overwrite, stop_revision=stop_revision,
3026
2661
            _override_hook_source_branch=self)
3027
2662
 
3028
2663
    def is_locked(self):
3038
2673
        # XXX: These should be returned by the set_last_revision_info verb
3039
2674
        old_revno, old_revid = self.last_revision_info()
3040
2675
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
3041
 
        if not revision_id or not isinstance(revision_id, basestring):
3042
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2676
        revision_id = ensure_null(revision_id)
3043
2677
        try:
3044
2678
            response = self._call('Branch.set_last_revision_info',
3045
2679
                self._remote_path(), self._lock_token, self._repo_lock_token,
3074
2708
            except errors.UnknownSmartMethod:
3075
2709
                medium._remember_remote_is_before((1, 6))
3076
2710
        self._clear_cached_state_of_remote_branch_only()
3077
 
        self._set_revision_history(self._lefthand_history(revision_id,
 
2711
        self.set_revision_history(self._lefthand_history(revision_id,
3078
2712
            last_rev=last_rev,other_branch=other_branch))
3079
2713
 
3080
2714
    def set_push_location(self, location):
3081
2715
        self._ensure_real()
3082
2716
        return self._real_branch.set_push_location(location)
3083
2717
 
3084
 
    def heads_to_fetch(self):
3085
 
        if self._format._use_default_local_heads_to_fetch():
3086
 
            # We recognise this format, and its heads-to-fetch implementation
3087
 
            # is the default one (tip + tags).  In this case it's cheaper to
3088
 
            # just use the default implementation rather than a special RPC as
3089
 
            # the tip and tags data is cached.
3090
 
            return branch.Branch.heads_to_fetch(self)
3091
 
        medium = self._client._medium
3092
 
        if medium._is_remote_before((2, 4)):
3093
 
            return self._vfs_heads_to_fetch()
3094
 
        try:
3095
 
            return self._rpc_heads_to_fetch()
3096
 
        except errors.UnknownSmartMethod:
3097
 
            medium._remember_remote_is_before((2, 4))
3098
 
            return self._vfs_heads_to_fetch()
3099
 
 
3100
 
    def _rpc_heads_to_fetch(self):
3101
 
        response = self._call('Branch.heads_to_fetch', self._remote_path())
3102
 
        if len(response) != 2:
3103
 
            raise errors.UnexpectedSmartServerResponse(response)
3104
 
        must_fetch, if_present_fetch = response
3105
 
        return set(must_fetch), set(if_present_fetch)
3106
 
 
3107
 
    def _vfs_heads_to_fetch(self):
3108
 
        self._ensure_real()
3109
 
        return self._real_branch.heads_to_fetch()
3110
 
 
3111
2718
 
3112
2719
class RemoteConfig(object):
3113
2720
    """A Config that reads and writes from smart verbs.
3127
2734
        """
3128
2735
        try:
3129
2736
            configobj = self._get_configobj()
3130
 
            section_obj = None
3131
2737
            if section is None:
3132
2738
                section_obj = configobj
3133
2739
            else:
3134
2740
                try:
3135
2741
                    section_obj = configobj[section]
3136
2742
                except KeyError:
3137
 
                    pass
3138
 
            if section_obj is None:
3139
 
                value = default
3140
 
            else:
3141
 
                value = section_obj.get(name, default)
 
2743
                    return default
 
2744
            return section_obj.get(name, default)
3142
2745
        except errors.UnknownSmartMethod:
3143
 
            value = self._vfs_get_option(name, section, default)
3144
 
        for hook in config.OldConfigHooks['get']:
3145
 
            hook(self, name, value)
3146
 
        return value
 
2746
            return self._vfs_get_option(name, section, default)
3147
2747
 
3148
2748
    def _response_to_configobj(self, response):
3149
2749
        if len(response[0]) and response[0][0] != 'ok':
3150
2750
            raise errors.UnexpectedSmartServerResponse(response)
3151
2751
        lines = response[1].read_body_bytes().splitlines()
3152
 
        conf = config.ConfigObj(lines, encoding='utf-8')
3153
 
        for hook in config.OldConfigHooks['load']:
3154
 
            hook(self)
3155
 
        return conf
 
2752
        return config.ConfigObj(lines, encoding='utf-8')
3156
2753
 
3157
2754
 
3158
2755
class RemoteBranchConfig(RemoteConfig):
3177
2774
        medium = self._branch._client._medium
3178
2775
        if medium._is_remote_before((1, 14)):
3179
2776
            return self._vfs_set_option(value, name, section)
3180
 
        if isinstance(value, dict):
3181
 
            if medium._is_remote_before((2, 2)):
3182
 
                return self._vfs_set_option(value, name, section)
3183
 
            return self._set_config_option_dict(value, name, section)
3184
 
        else:
3185
 
            return self._set_config_option(value, name, section)
3186
 
 
3187
 
    def _set_config_option(self, value, name, section):
3188
2777
        try:
3189
2778
            path = self._branch._remote_path()
3190
2779
            response = self._branch._client.call('Branch.set_config_option',
3191
2780
                path, self._branch._lock_token, self._branch._repo_lock_token,
3192
2781
                value.encode('utf8'), name, section or '')
3193
2782
        except errors.UnknownSmartMethod:
3194
 
            medium = self._branch._client._medium
3195
2783
            medium._remember_remote_is_before((1, 14))
3196
2784
            return self._vfs_set_option(value, name, section)
3197
2785
        if response != ():
3198
2786
            raise errors.UnexpectedSmartServerResponse(response)
3199
2787
 
3200
 
    def _serialize_option_dict(self, option_dict):
3201
 
        utf8_dict = {}
3202
 
        for key, value in option_dict.items():
3203
 
            if isinstance(key, unicode):
3204
 
                key = key.encode('utf8')
3205
 
            if isinstance(value, unicode):
3206
 
                value = value.encode('utf8')
3207
 
            utf8_dict[key] = value
3208
 
        return bencode.bencode(utf8_dict)
3209
 
 
3210
 
    def _set_config_option_dict(self, value, name, section):
3211
 
        try:
3212
 
            path = self._branch._remote_path()
3213
 
            serialised_dict = self._serialize_option_dict(value)
3214
 
            response = self._branch._client.call(
3215
 
                'Branch.set_config_option_dict',
3216
 
                path, self._branch._lock_token, self._branch._repo_lock_token,
3217
 
                serialised_dict, name, section or '')
3218
 
        except errors.UnknownSmartMethod:
3219
 
            medium = self._branch._client._medium
3220
 
            medium._remember_remote_is_before((2, 2))
3221
 
            return self._vfs_set_option(value, name, section)
3222
 
        if response != ():
3223
 
            raise errors.UnexpectedSmartServerResponse(response)
3224
 
 
3225
2788
    def _real_object(self):
3226
2789
        self._branch._ensure_real()
3227
2790
        return self._branch._real_branch
3310
2873
                    'Missing key %r in context %r', key_err.args[0], context)
3311
2874
                raise err
3312
2875
 
3313
 
    if err.error_verb == 'NoSuchRevision':
 
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':
3314
2880
        raise NoSuchRevision(find('branch'), err.error_args[0])
3315
2881
    elif err.error_verb == 'nosuchrevision':
3316
2882
        raise NoSuchRevision(find('repository'), err.error_args[0])
3323
2889
            detail=extra)
3324
2890
    elif err.error_verb == 'norepository':
3325
2891
        raise errors.NoRepositoryPresent(find('bzrdir'))
 
2892
    elif err.error_verb == 'LockContention':
 
2893
        raise errors.LockContention('(remote lock)')
3326
2894
    elif err.error_verb == 'UnlockableTransport':
3327
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])
3328
2898
    elif err.error_verb == 'TokenMismatch':
3329
2899
        raise errors.TokenMismatch(find('token'), '(remote token)')
3330
2900
    elif err.error_verb == 'Diverged':
3331
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)
3332
2908
    elif err.error_verb == 'NotStacked':
3333
2909
        raise errors.NotStacked(branch=find('branch'))
3334
2910
    elif err.error_verb == 'PermissionDenied':
3344
2920
    elif err.error_verb == 'NoSuchFile':
3345
2921
        path = get_path()
3346
2922
        raise errors.NoSuchFile(path)
3347
 
    _translate_error_without_context(err)
3348
 
 
3349
 
 
3350
 
def _translate_error_without_context(err):
3351
 
    """Translate any ErrorFromSmartServer values that don't require context"""
3352
 
    if err.error_verb == 'IncompatibleRepositories':
3353
 
        raise errors.IncompatibleRepositories(err.error_args[0],
3354
 
            err.error_args[1], err.error_args[2])
3355
 
    elif err.error_verb == 'LockContention':
3356
 
        raise errors.LockContention('(remote lock)')
3357
 
    elif err.error_verb == 'LockFailed':
3358
 
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
3359
 
    elif err.error_verb == 'TipChangeRejected':
3360
 
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
3361
 
    elif err.error_verb == 'UnstackableBranchFormat':
3362
 
        raise errors.UnstackableBranchFormat(*err.error_args)
3363
 
    elif err.error_verb == 'UnstackableRepositoryFormat':
3364
 
        raise errors.UnstackableRepositoryFormat(*err.error_args)
3365
2923
    elif err.error_verb == 'FileExists':
3366
2924
        raise errors.FileExists(err.error_args[0])
3367
2925
    elif err.error_verb == 'DirectoryNotEmpty':
3386
2944
            raise UnicodeEncodeError(encoding, val, start, end, reason)
3387
2945
    elif err.error_verb == 'ReadOnlyError':
3388
2946
        raise errors.TransportNotPossible('readonly transport')
3389
 
    elif err.error_verb == 'MemoryError':
3390
 
        raise errors.BzrError("remote server out of memory\n"
3391
 
            "Retry non-remotely, or contact the server admin for details.")
3392
2947
    raise errors.UnknownErrorFromSmartServer(err)