~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-05-07 08:01:38 UTC
  • mfrom: (5187.2.12 549310-mandatory-whoami)
  • Revision ID: pqm@pqm.ubuntu.com-20100507080138-doe642wej2tu47t5
(parthm) trivial docstring fix to indicate when errors.NoWhoami is raised.

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,
42
42
    )
43
43
from bzrlib.lockable_files import LockableFiles
44
44
from bzrlib.smart import client, vfs, repository as smart_repo
45
 
from bzrlib.smart.client import _SmartClient
46
 
from bzrlib.revision import NULL_REVISION
47
 
from bzrlib.repository import RepositoryWriteLockResult, _LazyListJoin
 
45
from bzrlib.revision import ensure_null, NULL_REVISION
48
46
from bzrlib.trace import mutter, note, warning
49
47
 
50
48
 
51
 
_DEFAULT_SEARCH_DEPTH = 100
52
 
 
53
 
 
54
49
class _RpcHelper(object):
55
50
    """Mixin class that helps with issuing RPCs."""
56
51
 
91
86
    return format
92
87
 
93
88
 
94
 
# Note that RemoteBzrDirProber lives in bzrlib.bzrdir so bzrlib.remote
95
 
# does not have to be imported unless a remote format is involved.
96
 
 
97
 
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
98
 
    """Format representing bzrdirs accessed via a smart server"""
99
 
 
100
 
    supports_workingtrees = False
101
 
 
102
 
    def __init__(self):
103
 
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
104
 
        # XXX: It's a bit ugly that the network name is here, because we'd
105
 
        # like to believe that format objects are stateless or at least
106
 
        # immutable,  However, we do at least avoid mutating the name after
107
 
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
108
 
        self._network_name = None
109
 
 
110
 
    def __repr__(self):
111
 
        return "%s(_network_name=%r)" % (self.__class__.__name__,
112
 
            self._network_name)
113
 
 
114
 
    def get_format_description(self):
115
 
        if self._network_name:
116
 
            real_format = controldir.network_format_registry.get(self._network_name)
117
 
            return 'Remote: ' + real_format.get_format_description()
118
 
        return 'bzr remote bzrdir'
119
 
 
120
 
    def get_format_string(self):
121
 
        raise NotImplementedError(self.get_format_string)
122
 
 
123
 
    def network_name(self):
124
 
        if self._network_name:
125
 
            return self._network_name
126
 
        else:
127
 
            raise AssertionError("No network name set.")
128
 
 
129
 
    def initialize_on_transport(self, transport):
130
 
        try:
131
 
            # hand off the request to the smart server
132
 
            client_medium = transport.get_smart_medium()
133
 
        except errors.NoSmartMedium:
134
 
            # TODO: lookup the local format from a server hint.
135
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
136
 
            return local_dir_format.initialize_on_transport(transport)
137
 
        client = _SmartClient(client_medium)
138
 
        path = client.remote_path_from_transport(transport)
139
 
        try:
140
 
            response = client.call('BzrDirFormat.initialize', path)
141
 
        except errors.ErrorFromSmartServer, err:
142
 
            _translate_error(err, path=path)
143
 
        if response[0] != 'ok':
144
 
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
145
 
        format = RemoteBzrDirFormat()
146
 
        self._supply_sub_formats_to(format)
147
 
        return RemoteBzrDir(transport, format)
148
 
 
149
 
    def parse_NoneTrueFalse(self, arg):
150
 
        if not arg:
151
 
            return None
152
 
        if arg == 'False':
153
 
            return False
154
 
        if arg == 'True':
155
 
            return True
156
 
        raise AssertionError("invalid arg %r" % arg)
157
 
 
158
 
    def _serialize_NoneTrueFalse(self, arg):
159
 
        if arg is False:
160
 
            return 'False'
161
 
        if arg:
162
 
            return 'True'
163
 
        return ''
164
 
 
165
 
    def _serialize_NoneString(self, arg):
166
 
        return arg or ''
167
 
 
168
 
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
169
 
        create_prefix=False, force_new_repo=False, stacked_on=None,
170
 
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
171
 
        shared_repo=False):
172
 
        try:
173
 
            # hand off the request to the smart server
174
 
            client_medium = transport.get_smart_medium()
175
 
        except errors.NoSmartMedium:
176
 
            do_vfs = True
177
 
        else:
178
 
            # Decline to open it if the server doesn't support our required
179
 
            # version (3) so that the VFS-based transport will do it.
180
 
            if client_medium.should_probe():
181
 
                try:
182
 
                    server_version = client_medium.protocol_version()
183
 
                    if server_version != '2':
184
 
                        do_vfs = True
185
 
                    else:
186
 
                        do_vfs = False
187
 
                except errors.SmartProtocolError:
188
 
                    # Apparently there's no usable smart server there, even though
189
 
                    # the medium supports the smart protocol.
190
 
                    do_vfs = True
191
 
            else:
192
 
                do_vfs = False
193
 
        if not do_vfs:
194
 
            client = _SmartClient(client_medium)
195
 
            path = client.remote_path_from_transport(transport)
196
 
            if client_medium._is_remote_before((1, 16)):
197
 
                do_vfs = True
198
 
        if do_vfs:
199
 
            # TODO: lookup the local format from a server hint.
200
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
201
 
            self._supply_sub_formats_to(local_dir_format)
202
 
            return local_dir_format.initialize_on_transport_ex(transport,
203
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
204
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
205
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
206
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
207
 
                vfs_only=True)
208
 
        return self._initialize_on_transport_ex_rpc(client, path, transport,
209
 
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
210
 
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
211
 
 
212
 
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
213
 
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
214
 
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
215
 
        args = []
216
 
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
217
 
        args.append(self._serialize_NoneTrueFalse(create_prefix))
218
 
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
219
 
        args.append(self._serialize_NoneString(stacked_on))
220
 
        # stack_on_pwd is often/usually our transport
221
 
        if stack_on_pwd:
222
 
            try:
223
 
                stack_on_pwd = transport.relpath(stack_on_pwd)
224
 
                if not stack_on_pwd:
225
 
                    stack_on_pwd = '.'
226
 
            except errors.PathNotChild:
227
 
                pass
228
 
        args.append(self._serialize_NoneString(stack_on_pwd))
229
 
        args.append(self._serialize_NoneString(repo_format_name))
230
 
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
231
 
        args.append(self._serialize_NoneTrueFalse(shared_repo))
232
 
        request_network_name = self._network_name or \
233
 
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
234
 
        try:
235
 
            response = client.call('BzrDirFormat.initialize_ex_1.16',
236
 
                request_network_name, path, *args)
237
 
        except errors.UnknownSmartMethod:
238
 
            client._medium._remember_remote_is_before((1,16))
239
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
240
 
            self._supply_sub_formats_to(local_dir_format)
241
 
            return local_dir_format.initialize_on_transport_ex(transport,
242
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
243
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
244
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
245
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
246
 
                vfs_only=True)
247
 
        except errors.ErrorFromSmartServer, err:
248
 
            _translate_error(err, path=path)
249
 
        repo_path = response[0]
250
 
        bzrdir_name = response[6]
251
 
        require_stacking = response[7]
252
 
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
253
 
        format = RemoteBzrDirFormat()
254
 
        format._network_name = bzrdir_name
255
 
        self._supply_sub_formats_to(format)
256
 
        bzrdir = RemoteBzrDir(transport, format, _client=client)
257
 
        if repo_path:
258
 
            repo_format = response_tuple_to_repo_format(response[1:])
259
 
            if repo_path == '.':
260
 
                repo_path = ''
261
 
            if repo_path:
262
 
                repo_bzrdir_format = RemoteBzrDirFormat()
263
 
                repo_bzrdir_format._network_name = response[5]
264
 
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
265
 
                    repo_bzrdir_format)
266
 
            else:
267
 
                repo_bzr = bzrdir
268
 
            final_stack = response[8] or None
269
 
            final_stack_pwd = response[9] or None
270
 
            if final_stack_pwd:
271
 
                final_stack_pwd = urlutils.join(
272
 
                    transport.base, final_stack_pwd)
273
 
            remote_repo = RemoteRepository(repo_bzr, repo_format)
274
 
            if len(response) > 10:
275
 
                # Updated server verb that locks remotely.
276
 
                repo_lock_token = response[10] or None
277
 
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
278
 
                if repo_lock_token:
279
 
                    remote_repo.dont_leave_lock_in_place()
280
 
            else:
281
 
                remote_repo.lock_write()
282
 
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
283
 
                final_stack_pwd, require_stacking)
284
 
            policy.acquire_repository()
285
 
        else:
286
 
            remote_repo = None
287
 
            policy = None
288
 
        bzrdir._format.set_branch_format(self.get_branch_format())
289
 
        if require_stacking:
290
 
            # The repo has already been created, but we need to make sure that
291
 
            # we'll make a stackable branch.
292
 
            bzrdir._format.require_stacking(_skip_repo=True)
293
 
        return remote_repo, bzrdir, require_stacking, policy
294
 
 
295
 
    def _open(self, transport):
296
 
        return RemoteBzrDir(transport, self)
297
 
 
298
 
    def __eq__(self, other):
299
 
        if not isinstance(other, RemoteBzrDirFormat):
300
 
            return False
301
 
        return self.get_format_description() == other.get_format_description()
302
 
 
303
 
    def __return_repository_format(self):
304
 
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
305
 
        # repository format has been asked for, tell the RemoteRepositoryFormat
306
 
        # that it should use that for init() etc.
307
 
        result = RemoteRepositoryFormat()
308
 
        custom_format = getattr(self, '_repository_format', None)
309
 
        if custom_format:
310
 
            if isinstance(custom_format, RemoteRepositoryFormat):
311
 
                return custom_format
312
 
            else:
313
 
                # We will use the custom format to create repositories over the
314
 
                # wire; expose its details like rich_root_data for code to
315
 
                # query
316
 
                result._custom_format = custom_format
317
 
        return result
318
 
 
319
 
    def get_branch_format(self):
320
 
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
321
 
        if not isinstance(result, RemoteBranchFormat):
322
 
            new_result = RemoteBranchFormat()
323
 
            new_result._custom_format = result
324
 
            # cache the result
325
 
            self.set_branch_format(new_result)
326
 
            result = new_result
327
 
        return result
328
 
 
329
 
    repository_format = property(__return_repository_format,
330
 
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
331
 
 
332
 
 
333
 
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
 
89
# Note: RemoteBzrDirFormat is in bzrdir.py
 
90
 
 
91
class RemoteBzrDir(BzrDir, _RpcHelper):
334
92
    """Control directory on a remote server, accessed via bzr:// or similar."""
335
93
 
336
94
    def __init__(self, transport, format, _client=None, _force_probe=False):
339
97
        :param _client: Private parameter for testing. Disables probing and the
340
98
            use of a real bzrdir.
341
99
        """
342
 
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
 
100
        BzrDir.__init__(self, transport, format)
343
101
        # this object holds a delegated bzrdir that uses file-level operations
344
102
        # to talk to the other side
345
103
        self._real_bzrdir = None
405
163
                import traceback
406
164
                warning('VFS BzrDir access triggered\n%s',
407
165
                    ''.join(traceback.format_stack()))
408
 
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
 
166
            self._real_bzrdir = BzrDir.open_from_transport(
409
167
                self.root_transport, _server_formats=False)
410
168
            self._format._network_name = \
411
169
                self._real_bzrdir._format.network_name()
417
175
        # Prevent aliasing problems in the next_open_branch_result cache.
418
176
        # See create_branch for rationale.
419
177
        self._next_open_branch_result = None
420
 
        return _mod_bzrdir.BzrDir.break_lock(self)
 
178
        return BzrDir.break_lock(self)
421
179
 
422
180
    def _vfs_cloning_metadir(self, require_stacking=False):
423
181
        self._ensure_real()
454
212
        if len(branch_info) != 2:
455
213
            raise errors.UnexpectedSmartServerResponse(response)
456
214
        branch_ref, branch_name = branch_info
457
 
        format = controldir.network_format_registry.get(control_name)
 
215
        format = bzrdir.network_format_registry.get(control_name)
458
216
        if repo_name:
459
 
            format.repository_format = _mod_repository.network_format_registry.get(
 
217
            format.repository_format = repository.network_format_registry.get(
460
218
                repo_name)
461
219
        if branch_ref == 'ref':
462
220
            # XXX: we need possible_transports here to avoid reopening the
463
221
            # connection to the referenced location
464
 
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
 
222
            ref_bzrdir = BzrDir.open(branch_name)
465
223
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
466
224
            format.set_branch_format(branch_format)
467
225
        elif branch_ref == 'branch':
486
244
        self._ensure_real()
487
245
        self._real_bzrdir.destroy_repository()
488
246
 
489
 
    def create_branch(self, name=None, repository=None,
490
 
                      append_revisions_only=None):
 
247
    def create_branch(self, name=None):
491
248
        # as per meta1 formats - just delegate to the format object which may
492
249
        # be parameterised.
493
250
        real_branch = self._format.get_branch_format().initialize(self,
494
 
            name=name, repository=repository,
495
 
            append_revisions_only=append_revisions_only)
 
251
            name=name)
496
252
        if not isinstance(real_branch, RemoteBranch):
497
 
            if not isinstance(repository, RemoteRepository):
498
 
                raise AssertionError(
499
 
                    'need a RemoteRepository to use with RemoteBranch, got %r'
500
 
                    % (repository,))
501
 
            result = RemoteBranch(self, repository, real_branch, name=name)
 
253
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
254
                                  name=name)
502
255
        else:
503
256
            result = real_branch
504
257
        # BzrDir.clone_on_transport() uses the result of create_branch but does
516
269
        self._real_bzrdir.destroy_branch(name=name)
517
270
        self._next_open_branch_result = None
518
271
 
519
 
    def create_workingtree(self, revision_id=None, from_branch=None,
520
 
        accelerator_tree=None, hardlink=False):
 
272
    def create_workingtree(self, revision_id=None, from_branch=None):
521
273
        raise errors.NotLocalUrl(self.transport.base)
522
274
 
523
 
    def find_branch_format(self, name=None):
 
275
    def find_branch_format(self):
524
276
        """Find the branch 'format' for this bzrdir.
525
277
 
526
278
        This might be a synthetic object for e.g. RemoteBranch and SVN.
527
279
        """
528
 
        b = self.open_branch(name=name)
 
280
        b = self.open_branch()
529
281
        return b._format
530
282
 
531
 
    def get_branch_reference(self, name=None):
 
283
    def get_branch_reference(self):
532
284
        """See BzrDir.get_branch_reference()."""
533
 
        if name is not None:
534
 
            # XXX JRV20100304: Support opening colocated branches
535
 
            raise errors.NoColocatedBranchSupport(self)
536
285
        response = self._get_branch_reference()
537
286
        if response[0] == 'ref':
538
287
            return response[1]
569
318
            raise errors.UnexpectedSmartServerResponse(response)
570
319
        return response
571
320
 
572
 
    def _get_tree_branch(self, name=None):
 
321
    def _get_tree_branch(self):
573
322
        """See BzrDir._get_tree_branch()."""
574
 
        return None, self.open_branch(name=name)
 
323
        return None, self.open_branch()
575
324
 
576
325
    def open_branch(self, name=None, unsupported=False,
577
326
                    ignore_fallbacks=False):
674
423
 
675
424
    def _path_for_remote_call(self, client):
676
425
        """Return the path to be used for this bzrdir in a remote call."""
677
 
        return urlutils.split_segment_parameters_raw(
678
 
            client.remote_path_from_transport(self.root_transport))[0]
 
426
        return client.remote_path_from_transport(self.root_transport)
679
427
 
680
428
    def get_branch_transport(self, branch_format, name=None):
681
429
        self._ensure_real()
693
441
        """Upgrading of remote bzrdirs is not supported yet."""
694
442
        return False
695
443
 
696
 
    def needs_format_conversion(self, format):
 
444
    def needs_format_conversion(self, format=None):
697
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)')
698
449
        return False
699
450
 
700
451
    def clone(self, url, revision_id=None, force_new_repo=False,
707
458
        return RemoteBzrDirConfig(self)
708
459
 
709
460
 
710
 
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
 
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
711
462
    """Format for repositories accessed over a _SmartClient.
712
463
 
713
464
    Instances of this repository are represented by RemoteRepository
728
479
    """
729
480
 
730
481
    _matchingbzrdir = RemoteBzrDirFormat()
731
 
    supports_full_versioned_files = True
732
 
    supports_leaving_lock = True
733
482
 
734
483
    def __init__(self):
735
 
        _mod_repository.RepositoryFormat.__init__(self)
 
484
        repository.RepositoryFormat.__init__(self)
736
485
        self._custom_format = None
737
486
        self._network_name = None
738
487
        self._creating_bzrdir = None
739
 
        self._revision_graph_can_have_wrong_parents = None
740
488
        self._supports_chks = None
741
489
        self._supports_external_lookups = None
742
490
        self._supports_tree_reference = None
743
 
        self._supports_funky_characters = None
744
491
        self._rich_root_data = None
745
492
 
746
493
    def __repr__(self):
775
522
        return self._supports_external_lookups
776
523
 
777
524
    @property
778
 
    def supports_funky_characters(self):
779
 
        if self._supports_funky_characters is None:
780
 
            self._ensure_real()
781
 
            self._supports_funky_characters = \
782
 
                self._custom_format.supports_funky_characters
783
 
        return self._supports_funky_characters
784
 
 
785
 
    @property
786
525
    def supports_tree_reference(self):
787
526
        if self._supports_tree_reference is None:
788
527
            self._ensure_real()
790
529
                self._custom_format.supports_tree_reference
791
530
        return self._supports_tree_reference
792
531
 
793
 
    @property
794
 
    def revision_graph_can_have_wrong_parents(self):
795
 
        if self._revision_graph_can_have_wrong_parents is None:
796
 
            self._ensure_real()
797
 
            self._revision_graph_can_have_wrong_parents = \
798
 
                self._custom_format.revision_graph_can_have_wrong_parents
799
 
        return self._revision_graph_can_have_wrong_parents
800
 
 
801
532
    def _vfs_initialize(self, a_bzrdir, shared):
802
533
        """Helper for common code in initialize."""
803
534
        if self._custom_format:
838
569
            network_name = self._network_name
839
570
        else:
840
571
            # Select the current bzrlib default and ask for that.
841
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
572
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
842
573
            reference_format = reference_bzrdir_format.repository_format
843
574
            network_name = reference_format.network_name()
844
575
        # 2) try direct creation via RPC
870
601
 
871
602
    def _ensure_real(self):
872
603
        if self._custom_format is None:
873
 
            self._custom_format = _mod_repository.network_format_registry.get(
 
604
            self._custom_format = repository.network_format_registry.get(
874
605
                self._network_name)
875
606
 
876
607
    @property
913
644
 
914
645
 
915
646
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
916
 
    controldir.ControlComponent):
 
647
    bzrdir.ControlComponent):
917
648
    """Repository accessed over rpc.
918
649
 
919
650
    For the moment most operations are performed using local transport-backed
972
703
        # transport, but I'm not sure it's worth making this method
973
704
        # optional -- mbp 2010-04-21
974
705
        return self.bzrdir.get_repository_transport(None)
975
 
 
 
706
        
976
707
    def __str__(self):
977
708
        return "%s(%s)" % (self.__class__.__name__, self.base)
978
709
 
1086
817
    def find_text_key_references(self):
1087
818
        """Find the text key references within the repository.
1088
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.
1089
823
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
1090
824
            to whether they were referred to by the inventory of the
1091
825
            revision_id that they contain. The inventory texts from all present
1109
843
        """Private method for using with old (< 1.2) servers to fallback."""
1110
844
        if revision_id is None:
1111
845
            revision_id = ''
1112
 
        elif _mod_revision.is_null(revision_id):
 
846
        elif revision.is_null(revision_id):
1113
847
            return {}
1114
848
 
1115
849
        path = self.bzrdir._path_for_remote_call(self._client)
1139
873
        return RemoteStreamSource(self, to_format)
1140
874
 
1141
875
    @needs_read_lock
1142
 
    def get_file_graph(self):
1143
 
        return graph.Graph(self.texts)
1144
 
 
1145
 
    @needs_read_lock
1146
876
    def has_revision(self, revision_id):
1147
877
        """True if this repository has a copy of the revision."""
1148
878
        # Copy of bzrlib.repository.Repository.has_revision
1165
895
    def _has_same_fallbacks(self, other_repo):
1166
896
        """Returns true if the repositories have the same fallbacks."""
1167
897
        # XXX: copied from Repository; it should be unified into a base class
1168
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
898
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
1169
899
        my_fb = self._fallback_repositories
1170
900
        other_fb = other_repo._fallback_repositories
1171
901
        if len(my_fb) != len(other_fb):
1200
930
        """See Repository.gather_stats()."""
1201
931
        path = self.bzrdir._path_for_remote_call(self._client)
1202
932
        # revid can be None to indicate no revisions, not just NULL_REVISION
1203
 
        if revid is None or _mod_revision.is_null(revid):
 
933
        if revid is None or revision.is_null(revid):
1204
934
            fmt_revid = ''
1205
935
        else:
1206
936
            fmt_revid = revid
1267
997
        pass
1268
998
 
1269
999
    def lock_read(self):
1270
 
        """Lock the repository for read operations.
1271
 
 
1272
 
        :return: A bzrlib.lock.LogicalLockResult.
1273
 
        """
1274
1000
        # wrong eventually - want a local lock cache context
1275
1001
        if not self._lock_mode:
1276
1002
            self._note_lock('r')
1283
1009
                repo.lock_read()
1284
1010
        else:
1285
1011
            self._lock_count += 1
1286
 
        return lock.LogicalLockResult(self.unlock)
1287
1012
 
1288
1013
    def _remote_lock_write(self, token):
1289
1014
        path = self.bzrdir._path_for_remote_call(self._client)
1329
1054
            raise errors.ReadOnlyError(self)
1330
1055
        else:
1331
1056
            self._lock_count += 1
1332
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
1057
        return self._lock_token or None
1333
1058
 
1334
1059
    def leave_lock_in_place(self):
1335
1060
        if not self._lock_token:
1489
1214
 
1490
1215
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1491
1216
                           timezone=None, committer=None, revprops=None,
1492
 
                           revision_id=None, lossy=False):
 
1217
                           revision_id=None):
1493
1218
        # FIXME: It ought to be possible to call this without immediately
1494
1219
        # triggering _ensure_real.  For now it's the easiest thing to do.
1495
1220
        self._ensure_real()
1496
1221
        real_repo = self._real_repository
1497
1222
        builder = real_repo.get_commit_builder(branch, parents,
1498
1223
                config, timestamp=timestamp, timezone=timezone,
1499
 
                committer=committer, revprops=revprops,
1500
 
                revision_id=revision_id, lossy=lossy)
 
1224
                committer=committer, revprops=revprops, revision_id=revision_id)
1501
1225
        return builder
1502
1226
 
1503
1227
    def add_fallback_repository(self, repository):
1511
1235
        # We need to accumulate additional repositories here, to pass them in
1512
1236
        # on various RPC's.
1513
1237
        #
1514
 
        # Make the check before we lock: this raises an exception.
1515
 
        self._check_fallback_repository(repository)
1516
1238
        if self.is_locked():
1517
1239
            # We will call fallback.unlock() when we transition to the unlocked
1518
1240
            # state, so always add a lock here. If a caller passes us a locked
1519
1241
            # repository, they are responsible for unlocking it later.
1520
1242
            repository.lock_read()
 
1243
        self._check_fallback_repository(repository)
1521
1244
        self._fallback_repositories.append(repository)
1522
1245
        # If self._real_repository was parameterised already (e.g. because a
1523
1246
        # _real_branch had its get_stacked_on_url method called), then the
1610
1333
        return result
1611
1334
 
1612
1335
    @needs_read_lock
1613
 
    def search_missing_revision_ids(self, other,
1614
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
1615
 
            find_ghosts=True, revision_ids=None, if_present_ids=None,
1616
 
            limit=None):
 
1336
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1617
1337
        """Return the revision ids that other has that this does not.
1618
1338
 
1619
1339
        These are returned in topological order.
1620
1340
 
1621
1341
        revision_id: only return revision ids included by revision_id.
1622
1342
        """
1623
 
        if symbol_versioning.deprecated_passed(revision_id):
1624
 
            symbol_versioning.warn(
1625
 
                'search_missing_revision_ids(revision_id=...) was '
1626
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
1627
 
                DeprecationWarning, stacklevel=2)
1628
 
            if revision_ids is not None:
1629
 
                raise AssertionError(
1630
 
                    'revision_ids is mutually exclusive with revision_id')
1631
 
            if revision_id is not None:
1632
 
                revision_ids = [revision_id]
1633
 
        inter_repo = _mod_repository.InterRepository.get(other, self)
1634
 
        return inter_repo.search_missing_revision_ids(
1635
 
            find_ghosts=find_ghosts, revision_ids=revision_ids,
1636
 
            if_present_ids=if_present_ids, limit=limit)
 
1343
        return repository.InterRepository.get(
 
1344
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
1637
1345
 
1638
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1346
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
1639
1347
            fetch_spec=None):
1640
1348
        # No base implementation to use as RemoteRepository is not a subclass
1641
1349
        # of Repository; so this is a copy of Repository.fetch().
1652
1360
            # check that last_revision is in 'from' and then return a
1653
1361
            # no-operation.
1654
1362
            if (revision_id is not None and
1655
 
                not _mod_revision.is_null(revision_id)):
 
1363
                not revision.is_null(revision_id)):
1656
1364
                self.get_revision(revision_id)
1657
1365
            return 0, []
1658
1366
        # if there is no specific appropriate InterRepository, this will get
1659
1367
        # the InterRepository base class, which raises an
1660
1368
        # IncompatibleRepositories when asked to fetch.
1661
 
        inter = _mod_repository.InterRepository.get(source, self)
1662
 
        return inter.fetch(revision_id=revision_id,
 
1369
        inter = repository.InterRepository.get(source, self)
 
1370
        return inter.fetch(revision_id=revision_id, pb=pb,
1663
1371
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1664
1372
 
1665
1373
    def create_bundle(self, target, base, fileobj, format=None):
1667
1375
        self._real_repository.create_bundle(target, base, fileobj, format)
1668
1376
 
1669
1377
    @needs_read_lock
1670
 
    @symbol_versioning.deprecated_method(
1671
 
        symbol_versioning.deprecated_in((2, 4, 0)))
1672
1378
    def get_ancestry(self, revision_id, topo_sorted=True):
1673
1379
        self._ensure_real()
1674
1380
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
1688
1394
        self._ensure_real()
1689
1395
        return self._real_repository.iter_files_bytes(desired_files)
1690
1396
 
1691
 
    def get_cached_parent_map(self, revision_ids):
1692
 
        """See bzrlib.CachingParentsProvider.get_cached_parent_map"""
1693
 
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
1694
 
 
1695
1397
    def get_parent_map(self, revision_ids):
1696
1398
        """See bzrlib.Graph.get_parent_map()."""
1697
1399
        return self._make_parents_provider().get_parent_map(revision_ids)
1755
1457
        if parents_map is None:
1756
1458
            # Repository is not locked, so there's no cache.
1757
1459
            parents_map = {}
1758
 
        if _DEFAULT_SEARCH_DEPTH <= 0:
1759
 
            (start_set, stop_keys,
1760
 
             key_count) = graph.search_result_from_parent_map(
1761
 
                parents_map, self._unstacked_provider.missing_keys)
1762
 
        else:
1763
 
            (start_set, stop_keys,
1764
 
             key_count) = graph.limited_search_result_from_parent_map(
1765
 
                parents_map, self._unstacked_provider.missing_keys,
1766
 
                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)
1767
1480
        recipe = ('manual', start_set, stop_keys, key_count)
1768
1481
        body = self._serialise_search_recipe(recipe)
1769
1482
        path = self.bzrdir._path_for_remote_call(self._client)
1884
1597
            tmpdir = osutils.mkdtemp()
1885
1598
            try:
1886
1599
                _extract_tar(tar, tmpdir)
1887
 
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
 
1600
                tmp_bzrdir = BzrDir.open(tmpdir)
1888
1601
                tmp_repo = tmp_bzrdir.open_repository()
1889
1602
                tmp_repo.copy_content_into(destination, revision_id)
1890
1603
            finally:
1975
1688
    def supports_rich_root(self):
1976
1689
        return self._format.rich_root_data
1977
1690
 
1978
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
1979
1691
    def iter_reverse_revision_history(self, revision_id):
1980
1692
        self._ensure_real()
1981
1693
        return self._real_repository.iter_reverse_revision_history(revision_id)
2002
1714
        return self._real_repository.item_keys_introduced_by(revision_ids,
2003
1715
            _files_pb=_files_pb)
2004
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
 
2005
1722
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2006
1723
        self._ensure_real()
2007
1724
        return self._real_repository._find_inconsistent_revision_parents(
2015
1732
        providers = [self._unstacked_provider]
2016
1733
        if other is not None:
2017
1734
            providers.insert(0, other)
2018
 
        return graph.StackedParentsProvider(_LazyListJoin(
2019
 
            providers, self._fallback_repositories))
 
1735
        providers.extend(r._make_parents_provider() for r in
 
1736
                         self._fallback_repositories)
 
1737
        return graph.StackedParentsProvider(providers)
2020
1738
 
2021
1739
    def _serialise_search_recipe(self, recipe):
2022
1740
        """Serialise a graph search recipe.
2030
1748
        return '\n'.join((start_keys, stop_keys, count))
2031
1749
 
2032
1750
    def _serialise_search_result(self, search_result):
2033
 
        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)]
2034
1757
        return '\n'.join(parts)
2035
1758
 
2036
1759
    def autopack(self):
2046
1769
            raise errors.UnexpectedSmartServerResponse(response)
2047
1770
 
2048
1771
 
2049
 
class RemoteStreamSink(vf_repository.StreamSink):
 
1772
class RemoteStreamSink(repository.StreamSink):
2050
1773
 
2051
1774
    def _insert_real(self, stream, src_format, resume_tokens):
2052
1775
        self.target_repo._ensure_real()
2153
1876
        self._last_substream and self._last_stream so that the stream can be
2154
1877
        resumed by _resume_stream_with_vfs.
2155
1878
        """
2156
 
 
 
1879
                    
2157
1880
        stream_iter = iter(stream)
2158
1881
        for substream_kind, substream in stream_iter:
2159
1882
            if substream_kind == 'inventory-deltas':
2162
1885
                return
2163
1886
            else:
2164
1887
                yield substream_kind, substream
2165
 
 
2166
 
 
2167
 
class RemoteStreamSource(vf_repository.StreamSource):
 
1888
            
 
1889
 
 
1890
class RemoteStreamSource(repository.StreamSource):
2168
1891
    """Stream data from a remote server."""
2169
1892
 
2170
1893
    def get_stream(self, search):
2230
1953
        candidate_verbs = [
2231
1954
            ('Repository.get_stream_1.19', (1, 19)),
2232
1955
            ('Repository.get_stream', (1, 13))]
2233
 
 
2234
1956
        found_verb = False
2235
1957
        for verb, version in candidate_verbs:
2236
1958
            if medium._is_remote_before(version):
2240
1962
                    verb, args, search_bytes)
2241
1963
            except errors.UnknownSmartMethod:
2242
1964
                medium._remember_remote_is_before(version)
2243
 
            except errors.UnknownErrorFromSmartServer, e:
2244
 
                if isinstance(search, graph.EverythingResult):
2245
 
                    error_verb = e.error_from_smart_server.error_verb
2246
 
                    if error_verb == 'BadSearch':
2247
 
                        # Pre-2.4 servers don't support this sort of search.
2248
 
                        # XXX: perhaps falling back to VFS on BadSearch is a
2249
 
                        # good idea in general?  It might provide a little bit
2250
 
                        # of protection against client-side bugs.
2251
 
                        medium._remember_remote_is_before((2, 4))
2252
 
                        break
2253
 
                raise
2254
1965
            else:
2255
1966
                response_tuple, response_handler = response
2256
1967
                found_verb = True
2260
1971
        if response_tuple[0] != 'ok':
2261
1972
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2262
1973
        byte_stream = response_handler.read_streamed_body()
2263
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
2264
 
            self._record_counter)
 
1974
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
2265
1975
        if src_format.network_name() != repo._format.network_name():
2266
1976
            raise AssertionError(
2267
1977
                "Mismatched RemoteRepository and stream src %r, %r" % (
2353
2063
        return a_bzrdir.open_branch(name=name, 
2354
2064
            ignore_fallbacks=ignore_fallbacks)
2355
2065
 
2356
 
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
 
2066
    def _vfs_initialize(self, a_bzrdir, name):
2357
2067
        # Initialisation when using a local bzrdir object, or a non-vfs init
2358
2068
        # method is not available on the server.
2359
2069
        # self._custom_format is always set - the start of initialize ensures
2361
2071
        if isinstance(a_bzrdir, RemoteBzrDir):
2362
2072
            a_bzrdir._ensure_real()
2363
2073
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
2364
 
                name, append_revisions_only=append_revisions_only)
 
2074
                name)
2365
2075
        else:
2366
2076
            # We assume the bzrdir is parameterised; it may not be.
2367
 
            result = self._custom_format.initialize(a_bzrdir, name,
2368
 
                append_revisions_only=append_revisions_only)
 
2077
            result = self._custom_format.initialize(a_bzrdir, name)
2369
2078
        if (isinstance(a_bzrdir, RemoteBzrDir) and
2370
2079
            not isinstance(result, RemoteBranch)):
2371
2080
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
2372
2081
                                  name=name)
2373
2082
        return result
2374
2083
 
2375
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2376
 
                   append_revisions_only=None):
 
2084
    def initialize(self, a_bzrdir, name=None):
2377
2085
        # 1) get the network name to use.
2378
2086
        if self._custom_format:
2379
2087
            network_name = self._custom_format.network_name()
2380
2088
        else:
2381
2089
            # Select the current bzrlib default and ask for that.
2382
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
2090
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
2383
2091
            reference_format = reference_bzrdir_format.get_branch_format()
2384
2092
            self._custom_format = reference_format
2385
2093
            network_name = reference_format.network_name()
2386
2094
        # Being asked to create on a non RemoteBzrDir:
2387
2095
        if not isinstance(a_bzrdir, RemoteBzrDir):
2388
 
            return self._vfs_initialize(a_bzrdir, name=name,
2389
 
                append_revisions_only=append_revisions_only)
 
2096
            return self._vfs_initialize(a_bzrdir, name=name)
2390
2097
        medium = a_bzrdir._client._medium
2391
2098
        if medium._is_remote_before((1, 13)):
2392
 
            return self._vfs_initialize(a_bzrdir, name=name,
2393
 
                append_revisions_only=append_revisions_only)
 
2099
            return self._vfs_initialize(a_bzrdir, name=name)
2394
2100
        # Creating on a remote bzr dir.
2395
2101
        # 2) try direct creation via RPC
2396
2102
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2403
2109
        except errors.UnknownSmartMethod:
2404
2110
            # Fallback - use vfs methods
2405
2111
            medium._remember_remote_is_before((1, 13))
2406
 
            return self._vfs_initialize(a_bzrdir, name=name,
2407
 
                    append_revisions_only=append_revisions_only)
 
2112
            return self._vfs_initialize(a_bzrdir, name=name)
2408
2113
        if response[0] != 'ok':
2409
2114
            raise errors.UnexpectedSmartServerResponse(response)
2410
2115
        # Turn the response into a RemoteRepository object.
2411
2116
        format = RemoteBranchFormat(network_name=response[1])
2412
2117
        repo_format = response_tuple_to_repo_format(response[3:])
2413
 
        repo_path = response[2]
2414
 
        if repository is not None:
2415
 
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
2416
 
            url_diff = urlutils.relative_url(repository.user_url,
2417
 
                    remote_repo_url)
2418
 
            if url_diff != '.':
2419
 
                raise AssertionError(
2420
 
                    'repository.user_url %r does not match URL from server '
2421
 
                    'response (%r + %r)'
2422
 
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
2423
 
            remote_repo = repository
 
2118
        if response[2] == '':
 
2119
            repo_bzrdir = a_bzrdir
2424
2120
        else:
2425
 
            if repo_path == '':
2426
 
                repo_bzrdir = a_bzrdir
2427
 
            else:
2428
 
                repo_bzrdir = RemoteBzrDir(
2429
 
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
2430
 
                    a_bzrdir._client)
2431
 
            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)
2432
2125
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2433
2126
            format=format, setup_stacking=False, name=name)
2434
 
        if append_revisions_only:
2435
 
            remote_branch.set_append_revisions_only(append_revisions_only)
2436
2127
        # XXX: We know this is a new branch, so it must have revno 0, revid
2437
2128
        # NULL_REVISION. Creating the branch locked would make this be unable
2438
2129
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2457
2148
        self._ensure_real()
2458
2149
        return self._custom_format.supports_set_append_revisions_only()
2459
2150
 
2460
 
    def _use_default_local_heads_to_fetch(self):
2461
 
        # If the branch format is a metadir format *and* its heads_to_fetch
2462
 
        # implementation is not overridden vs the base class, we can use the
2463
 
        # base class logic rather than use the heads_to_fetch RPC.  This is
2464
 
        # usually cheaper in terms of net round trips, as the last-revision and
2465
 
        # tags info fetched is cached and would be fetched anyway.
2466
 
        self._ensure_real()
2467
 
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
2468
 
            branch_class = self._custom_format._branch_class()
2469
 
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
2470
 
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
2471
 
                return True
2472
 
        return False
2473
2151
 
2474
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2475
2153
    """Branch stored on a server accessed by HPSS RPC.
2633
2311
                self.bzrdir, self._client)
2634
2312
        return self._control_files
2635
2313
 
2636
 
    def _get_checkout_format(self, lightweight=False):
 
2314
    def _get_checkout_format(self):
2637
2315
        self._ensure_real()
2638
 
        if lightweight:
2639
 
            format = RemoteBzrDirFormat()
2640
 
            self.bzrdir._format._supply_sub_formats_to(format)
2641
 
            format.workingtree_format = self._real_branch._get_checkout_format(
2642
 
                lightweight=lightweight).workingtree_format
2643
 
            return format
2644
 
        else:
2645
 
            return self._real_branch._get_checkout_format(lightweight=False)
 
2316
        return self._real_branch._get_checkout_format()
2646
2317
 
2647
2318
    def get_physical_lock_status(self):
2648
2319
        """See Branch.get_physical_lock_status()."""
2681
2352
            self._is_stacked = False
2682
2353
        else:
2683
2354
            self._is_stacked = True
2684
 
 
 
2355
        
2685
2356
    def _vfs_get_tags_bytes(self):
2686
2357
        self._ensure_real()
2687
2358
        return self._real_branch._get_tags_bytes()
2688
2359
 
2689
 
    @needs_read_lock
2690
2360
    def _get_tags_bytes(self):
2691
 
        if self._tags_bytes is None:
2692
 
            self._tags_bytes = self._get_tags_bytes_via_hpss()
2693
 
        return self._tags_bytes
2694
 
 
2695
 
    def _get_tags_bytes_via_hpss(self):
2696
2361
        medium = self._client._medium
2697
2362
        if medium._is_remote_before((1, 13)):
2698
2363
            return self._vfs_get_tags_bytes()
2708
2373
        return self._real_branch._set_tags_bytes(bytes)
2709
2374
 
2710
2375
    def _set_tags_bytes(self, bytes):
2711
 
        if self.is_locked():
2712
 
            self._tags_bytes = bytes
2713
2376
        medium = self._client._medium
2714
2377
        if medium._is_remote_before((1, 18)):
2715
2378
            self._vfs_set_tags_bytes(bytes)
2724
2387
            self._vfs_set_tags_bytes(bytes)
2725
2388
 
2726
2389
    def lock_read(self):
2727
 
        """Lock the branch for read operations.
2728
 
 
2729
 
        :return: A bzrlib.lock.LogicalLockResult.
2730
 
        """
2731
2390
        self.repository.lock_read()
2732
2391
        if not self._lock_mode:
2733
2392
            self._note_lock('r')
2737
2396
                self._real_branch.lock_read()
2738
2397
        else:
2739
2398
            self._lock_count += 1
2740
 
        return lock.LogicalLockResult(self.unlock)
2741
2399
 
2742
2400
    def _remote_lock_write(self, token):
2743
2401
        if token is None:
2744
2402
            branch_token = repo_token = ''
2745
2403
        else:
2746
2404
            branch_token = token
2747
 
            repo_token = self.repository.lock_write().repository_token
 
2405
            repo_token = self.repository.lock_write()
2748
2406
            self.repository.unlock()
2749
2407
        err_context = {'token': token}
2750
 
        try:
2751
 
            response = self._call(
2752
 
                'Branch.lock_write', self._remote_path(), branch_token,
2753
 
                repo_token or '', **err_context)
2754
 
        except errors.LockContention, e:
2755
 
            # The LockContention from the server doesn't have any
2756
 
            # information about the lock_url. We re-raise LockContention
2757
 
            # with valid lock_url.
2758
 
            raise errors.LockContention('(remote lock)',
2759
 
                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)
2760
2411
        if response[0] != 'ok':
2761
2412
            raise errors.UnexpectedSmartServerResponse(response)
2762
2413
        ok, branch_token, repo_token = response
2783
2434
            self._lock_mode = 'w'
2784
2435
            self._lock_count = 1
2785
2436
        elif self._lock_mode == 'r':
2786
 
            raise errors.ReadOnlyError(self)
 
2437
            raise errors.ReadOnlyTransaction
2787
2438
        else:
2788
2439
            if token is not None:
2789
2440
                # A token was given to lock_write, and we're relocking, so
2794
2445
            self._lock_count += 1
2795
2446
            # Re-lock the repository too.
2796
2447
            self.repository.lock_write(self._repo_lock_token)
2797
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2448
        return self._lock_token or None
2798
2449
 
2799
2450
    def _unlock(self, branch_token, repo_token):
2800
2451
        err_context = {'token': str((branch_token, repo_token))}
2869
2520
            missing_parent = parent_map[missing_parent]
2870
2521
        raise errors.RevisionNotPresent(missing_parent, self.repository)
2871
2522
 
2872
 
    def _read_last_revision_info(self):
 
2523
    def _last_revision_info(self):
2873
2524
        response = self._call('Branch.last_revision_info', self._remote_path())
2874
2525
        if response[0] != 'ok':
2875
2526
            raise SmartProtocolError('unexpected response code %s' % (response,))
2938
2589
            raise errors.UnexpectedSmartServerResponse(response)
2939
2590
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2940
2591
 
2941
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2942
2592
    @needs_write_lock
2943
2593
    def set_revision_history(self, rev_history):
2944
 
        """See Branch.set_revision_history."""
2945
 
        self._set_revision_history(rev_history)
2946
 
 
2947
 
    @needs_write_lock
2948
 
    def _set_revision_history(self, rev_history):
2949
2594
        # Send just the tip revision of the history; the server will generate
2950
2595
        # the full history from that.  If the revision doesn't exist in this
2951
2596
        # branch, NoSuchRevision will be raised.
3009
2654
            _override_hook_target=self, **kwargs)
3010
2655
 
3011
2656
    @needs_read_lock
3012
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
 
2657
    def push(self, target, overwrite=False, stop_revision=None):
3013
2658
        self._ensure_real()
3014
2659
        return self._real_branch.push(
3015
 
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
 
2660
            target, overwrite=overwrite, stop_revision=stop_revision,
3016
2661
            _override_hook_source_branch=self)
3017
2662
 
3018
2663
    def is_locked(self):
3028
2673
        # XXX: These should be returned by the set_last_revision_info verb
3029
2674
        old_revno, old_revid = self.last_revision_info()
3030
2675
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
3031
 
        if not revision_id or not isinstance(revision_id, basestring):
3032
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2676
        revision_id = ensure_null(revision_id)
3033
2677
        try:
3034
2678
            response = self._call('Branch.set_last_revision_info',
3035
2679
                self._remote_path(), self._lock_token, self._repo_lock_token,
3064
2708
            except errors.UnknownSmartMethod:
3065
2709
                medium._remember_remote_is_before((1, 6))
3066
2710
        self._clear_cached_state_of_remote_branch_only()
3067
 
        self._set_revision_history(self._lefthand_history(revision_id,
 
2711
        self.set_revision_history(self._lefthand_history(revision_id,
3068
2712
            last_rev=last_rev,other_branch=other_branch))
3069
2713
 
3070
2714
    def set_push_location(self, location):
3071
2715
        self._ensure_real()
3072
2716
        return self._real_branch.set_push_location(location)
3073
2717
 
3074
 
    def heads_to_fetch(self):
3075
 
        if self._format._use_default_local_heads_to_fetch():
3076
 
            # We recognise this format, and its heads-to-fetch implementation
3077
 
            # is the default one (tip + tags).  In this case it's cheaper to
3078
 
            # just use the default implementation rather than a special RPC as
3079
 
            # the tip and tags data is cached.
3080
 
            return branch.Branch.heads_to_fetch(self)
3081
 
        medium = self._client._medium
3082
 
        if medium._is_remote_before((2, 4)):
3083
 
            return self._vfs_heads_to_fetch()
3084
 
        try:
3085
 
            return self._rpc_heads_to_fetch()
3086
 
        except errors.UnknownSmartMethod:
3087
 
            medium._remember_remote_is_before((2, 4))
3088
 
            return self._vfs_heads_to_fetch()
3089
 
 
3090
 
    def _rpc_heads_to_fetch(self):
3091
 
        response = self._call('Branch.heads_to_fetch', self._remote_path())
3092
 
        if len(response) != 2:
3093
 
            raise errors.UnexpectedSmartServerResponse(response)
3094
 
        must_fetch, if_present_fetch = response
3095
 
        return set(must_fetch), set(if_present_fetch)
3096
 
 
3097
 
    def _vfs_heads_to_fetch(self):
3098
 
        self._ensure_real()
3099
 
        return self._real_branch.heads_to_fetch()
3100
 
 
3101
2718
 
3102
2719
class RemoteConfig(object):
3103
2720
    """A Config that reads and writes from smart verbs.
3117
2734
        """
3118
2735
        try:
3119
2736
            configobj = self._get_configobj()
3120
 
            section_obj = None
3121
2737
            if section is None:
3122
2738
                section_obj = configobj
3123
2739
            else:
3124
2740
                try:
3125
2741
                    section_obj = configobj[section]
3126
2742
                except KeyError:
3127
 
                    pass
3128
 
            if section_obj is None:
3129
 
                value = default
3130
 
            else:
3131
 
                value = section_obj.get(name, default)
 
2743
                    return default
 
2744
            return section_obj.get(name, default)
3132
2745
        except errors.UnknownSmartMethod:
3133
 
            value = self._vfs_get_option(name, section, default)
3134
 
        for hook in config.OldConfigHooks['get']:
3135
 
            hook(self, name, value)
3136
 
        return value
 
2746
            return self._vfs_get_option(name, section, default)
3137
2747
 
3138
2748
    def _response_to_configobj(self, response):
3139
2749
        if len(response[0]) and response[0][0] != 'ok':
3140
2750
            raise errors.UnexpectedSmartServerResponse(response)
3141
2751
        lines = response[1].read_body_bytes().splitlines()
3142
 
        conf = config.ConfigObj(lines, encoding='utf-8')
3143
 
        for hook in config.OldConfigHooks['load']:
3144
 
            hook(self)
3145
 
        return conf
 
2752
        return config.ConfigObj(lines, encoding='utf-8')
3146
2753
 
3147
2754
 
3148
2755
class RemoteBranchConfig(RemoteConfig):
3167
2774
        medium = self._branch._client._medium
3168
2775
        if medium._is_remote_before((1, 14)):
3169
2776
            return self._vfs_set_option(value, name, section)
3170
 
        if isinstance(value, dict):
3171
 
            if medium._is_remote_before((2, 2)):
3172
 
                return self._vfs_set_option(value, name, section)
3173
 
            return self._set_config_option_dict(value, name, section)
3174
 
        else:
3175
 
            return self._set_config_option(value, name, section)
3176
 
 
3177
 
    def _set_config_option(self, value, name, section):
3178
2777
        try:
3179
2778
            path = self._branch._remote_path()
3180
2779
            response = self._branch._client.call('Branch.set_config_option',
3181
2780
                path, self._branch._lock_token, self._branch._repo_lock_token,
3182
2781
                value.encode('utf8'), name, section or '')
3183
2782
        except errors.UnknownSmartMethod:
3184
 
            medium = self._branch._client._medium
3185
2783
            medium._remember_remote_is_before((1, 14))
3186
2784
            return self._vfs_set_option(value, name, section)
3187
2785
        if response != ():
3188
2786
            raise errors.UnexpectedSmartServerResponse(response)
3189
2787
 
3190
 
    def _serialize_option_dict(self, option_dict):
3191
 
        utf8_dict = {}
3192
 
        for key, value in option_dict.items():
3193
 
            if isinstance(key, unicode):
3194
 
                key = key.encode('utf8')
3195
 
            if isinstance(value, unicode):
3196
 
                value = value.encode('utf8')
3197
 
            utf8_dict[key] = value
3198
 
        return bencode.bencode(utf8_dict)
3199
 
 
3200
 
    def _set_config_option_dict(self, value, name, section):
3201
 
        try:
3202
 
            path = self._branch._remote_path()
3203
 
            serialised_dict = self._serialize_option_dict(value)
3204
 
            response = self._branch._client.call(
3205
 
                'Branch.set_config_option_dict',
3206
 
                path, self._branch._lock_token, self._branch._repo_lock_token,
3207
 
                serialised_dict, name, section or '')
3208
 
        except errors.UnknownSmartMethod:
3209
 
            medium = self._branch._client._medium
3210
 
            medium._remember_remote_is_before((2, 2))
3211
 
            return self._vfs_set_option(value, name, section)
3212
 
        if response != ():
3213
 
            raise errors.UnexpectedSmartServerResponse(response)
3214
 
 
3215
2788
    def _real_object(self):
3216
2789
        self._branch._ensure_real()
3217
2790
        return self._branch._real_branch
3300
2873
                    'Missing key %r in context %r', key_err.args[0], context)
3301
2874
                raise err
3302
2875
 
3303
 
    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':
3304
2880
        raise NoSuchRevision(find('branch'), err.error_args[0])
3305
2881
    elif err.error_verb == 'nosuchrevision':
3306
2882
        raise NoSuchRevision(find('repository'), err.error_args[0])
3313
2889
            detail=extra)
3314
2890
    elif err.error_verb == 'norepository':
3315
2891
        raise errors.NoRepositoryPresent(find('bzrdir'))
 
2892
    elif err.error_verb == 'LockContention':
 
2893
        raise errors.LockContention('(remote lock)')
3316
2894
    elif err.error_verb == 'UnlockableTransport':
3317
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])
3318
2898
    elif err.error_verb == 'TokenMismatch':
3319
2899
        raise errors.TokenMismatch(find('token'), '(remote token)')
3320
2900
    elif err.error_verb == 'Diverged':
3321
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)
3322
2908
    elif err.error_verb == 'NotStacked':
3323
2909
        raise errors.NotStacked(branch=find('branch'))
3324
2910
    elif err.error_verb == 'PermissionDenied':
3334
2920
    elif err.error_verb == 'NoSuchFile':
3335
2921
        path = get_path()
3336
2922
        raise errors.NoSuchFile(path)
3337
 
    _translate_error_without_context(err)
3338
 
 
3339
 
 
3340
 
def _translate_error_without_context(err):
3341
 
    """Translate any ErrorFromSmartServer values that don't require context"""
3342
 
    if err.error_verb == 'IncompatibleRepositories':
3343
 
        raise errors.IncompatibleRepositories(err.error_args[0],
3344
 
            err.error_args[1], err.error_args[2])
3345
 
    elif err.error_verb == 'LockContention':
3346
 
        raise errors.LockContention('(remote lock)')
3347
 
    elif err.error_verb == 'LockFailed':
3348
 
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
3349
 
    elif err.error_verb == 'TipChangeRejected':
3350
 
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
3351
 
    elif err.error_verb == 'UnstackableBranchFormat':
3352
 
        raise errors.UnstackableBranchFormat(*err.error_args)
3353
 
    elif err.error_verb == 'UnstackableRepositoryFormat':
3354
 
        raise errors.UnstackableRepositoryFormat(*err.error_args)
3355
2923
    elif err.error_verb == 'FileExists':
3356
2924
        raise errors.FileExists(err.error_args[0])
3357
2925
    elif err.error_verb == 'DirectoryNotEmpty':
3376
2944
            raise UnicodeEncodeError(encoding, val, start, end, reason)
3377
2945
    elif err.error_verb == 'ReadOnlyError':
3378
2946
        raise errors.TransportNotPossible('readonly transport')
3379
 
    elif err.error_verb == 'MemoryError':
3380
 
        raise errors.BzrError("remote server out of memory\n"
3381
 
            "Retry non-remotely, or contact the server admin for details.")
3382
2947
    raise errors.UnknownErrorFromSmartServer(err)