/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: John Arbash Meinel
  • Date: 2011-04-07 10:36:24 UTC
  • mfrom: (5764 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5766.
  • Revision ID: john@arbash-meinel.com-20110407103624-n76g6tjeqmznwdcd
Merge bzr.dev 5764 to resolve release-notes (aka NEWS) conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006-2011 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,
 
22
    bzrdir as _mod_bzrdir,
23
23
    config,
 
24
    controldir,
24
25
    debug,
25
26
    errors,
26
27
    graph,
27
28
    lock,
28
29
    lockdir,
29
 
    repository,
30
30
    repository as _mod_repository,
31
 
    revision,
32
31
    revision as _mod_revision,
33
32
    static_tuple,
34
33
    symbol_versioning,
 
34
    urlutils,
35
35
)
36
36
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
37
 
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
38
37
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
39
38
from bzrlib.errors import (
40
39
    NoSuchRevision,
42
41
    )
43
42
from bzrlib.lockable_files import LockableFiles
44
43
from bzrlib.smart import client, vfs, repository as smart_repo
45
 
from bzrlib.revision import ensure_null, NULL_REVISION
 
44
from bzrlib.smart.client import _SmartClient
 
45
from bzrlib.revision import NULL_REVISION
46
46
from bzrlib.repository import RepositoryWriteLockResult
47
47
from bzrlib.trace import mutter, note, warning
48
48
 
87
87
    return format
88
88
 
89
89
 
90
 
# Note: RemoteBzrDirFormat is in bzrdir.py
91
 
 
92
 
class RemoteBzrDir(BzrDir, _RpcHelper):
 
90
# Note that RemoteBzrDirProber lives in bzrlib.bzrdir so bzrlib.remote
 
91
# does not have to be imported unless a remote format is involved.
 
92
 
 
93
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
 
94
    """Format representing bzrdirs accessed via a smart server"""
 
95
 
 
96
    supports_workingtrees = False
 
97
 
 
98
    def __init__(self):
 
99
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
 
100
        # XXX: It's a bit ugly that the network name is here, because we'd
 
101
        # like to believe that format objects are stateless or at least
 
102
        # immutable,  However, we do at least avoid mutating the name after
 
103
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
 
104
        self._network_name = None
 
105
 
 
106
    def __repr__(self):
 
107
        return "%s(_network_name=%r)" % (self.__class__.__name__,
 
108
            self._network_name)
 
109
 
 
110
    def get_format_description(self):
 
111
        if self._network_name:
 
112
            real_format = controldir.network_format_registry.get(self._network_name)
 
113
            return 'Remote: ' + real_format.get_format_description()
 
114
        return 'bzr remote bzrdir'
 
115
 
 
116
    def get_format_string(self):
 
117
        raise NotImplementedError(self.get_format_string)
 
118
 
 
119
    def network_name(self):
 
120
        if self._network_name:
 
121
            return self._network_name
 
122
        else:
 
123
            raise AssertionError("No network name set.")
 
124
 
 
125
    def initialize_on_transport(self, transport):
 
126
        try:
 
127
            # hand off the request to the smart server
 
128
            client_medium = transport.get_smart_medium()
 
129
        except errors.NoSmartMedium:
 
130
            # TODO: lookup the local format from a server hint.
 
131
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
132
            return local_dir_format.initialize_on_transport(transport)
 
133
        client = _SmartClient(client_medium)
 
134
        path = client.remote_path_from_transport(transport)
 
135
        try:
 
136
            response = client.call('BzrDirFormat.initialize', path)
 
137
        except errors.ErrorFromSmartServer, err:
 
138
            _translate_error(err, path=path)
 
139
        if response[0] != 'ok':
 
140
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
 
141
        format = RemoteBzrDirFormat()
 
142
        self._supply_sub_formats_to(format)
 
143
        return RemoteBzrDir(transport, format)
 
144
 
 
145
    def parse_NoneTrueFalse(self, arg):
 
146
        if not arg:
 
147
            return None
 
148
        if arg == 'False':
 
149
            return False
 
150
        if arg == 'True':
 
151
            return True
 
152
        raise AssertionError("invalid arg %r" % arg)
 
153
 
 
154
    def _serialize_NoneTrueFalse(self, arg):
 
155
        if arg is False:
 
156
            return 'False'
 
157
        if arg:
 
158
            return 'True'
 
159
        return ''
 
160
 
 
161
    def _serialize_NoneString(self, arg):
 
162
        return arg or ''
 
163
 
 
164
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
165
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
166
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
167
        shared_repo=False):
 
168
        try:
 
169
            # hand off the request to the smart server
 
170
            client_medium = transport.get_smart_medium()
 
171
        except errors.NoSmartMedium:
 
172
            do_vfs = True
 
173
        else:
 
174
            # Decline to open it if the server doesn't support our required
 
175
            # version (3) so that the VFS-based transport will do it.
 
176
            if client_medium.should_probe():
 
177
                try:
 
178
                    server_version = client_medium.protocol_version()
 
179
                    if server_version != '2':
 
180
                        do_vfs = True
 
181
                    else:
 
182
                        do_vfs = False
 
183
                except errors.SmartProtocolError:
 
184
                    # Apparently there's no usable smart server there, even though
 
185
                    # the medium supports the smart protocol.
 
186
                    do_vfs = True
 
187
            else:
 
188
                do_vfs = False
 
189
        if not do_vfs:
 
190
            client = _SmartClient(client_medium)
 
191
            path = client.remote_path_from_transport(transport)
 
192
            if client_medium._is_remote_before((1, 16)):
 
193
                do_vfs = True
 
194
        if do_vfs:
 
195
            # TODO: lookup the local format from a server hint.
 
196
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
197
            self._supply_sub_formats_to(local_dir_format)
 
198
            return local_dir_format.initialize_on_transport_ex(transport,
 
199
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
200
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
201
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
202
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
203
                vfs_only=True)
 
204
        return self._initialize_on_transport_ex_rpc(client, path, transport,
 
205
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
 
206
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
 
207
 
 
208
    def _initialize_on_transport_ex_rpc(self, 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
        args = []
 
212
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
 
213
        args.append(self._serialize_NoneTrueFalse(create_prefix))
 
214
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
 
215
        args.append(self._serialize_NoneString(stacked_on))
 
216
        # stack_on_pwd is often/usually our transport
 
217
        if stack_on_pwd:
 
218
            try:
 
219
                stack_on_pwd = transport.relpath(stack_on_pwd)
 
220
                if not stack_on_pwd:
 
221
                    stack_on_pwd = '.'
 
222
            except errors.PathNotChild:
 
223
                pass
 
224
        args.append(self._serialize_NoneString(stack_on_pwd))
 
225
        args.append(self._serialize_NoneString(repo_format_name))
 
226
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
 
227
        args.append(self._serialize_NoneTrueFalse(shared_repo))
 
228
        request_network_name = self._network_name or \
 
229
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
 
230
        try:
 
231
            response = client.call('BzrDirFormat.initialize_ex_1.16',
 
232
                request_network_name, path, *args)
 
233
        except errors.UnknownSmartMethod:
 
234
            client._medium._remember_remote_is_before((1,16))
 
235
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
 
236
            self._supply_sub_formats_to(local_dir_format)
 
237
            return local_dir_format.initialize_on_transport_ex(transport,
 
238
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
239
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
240
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
241
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
242
                vfs_only=True)
 
243
        except errors.ErrorFromSmartServer, err:
 
244
            _translate_error(err, path=path)
 
245
        repo_path = response[0]
 
246
        bzrdir_name = response[6]
 
247
        require_stacking = response[7]
 
248
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
 
249
        format = RemoteBzrDirFormat()
 
250
        format._network_name = bzrdir_name
 
251
        self._supply_sub_formats_to(format)
 
252
        bzrdir = RemoteBzrDir(transport, format, _client=client)
 
253
        if repo_path:
 
254
            repo_format = response_tuple_to_repo_format(response[1:])
 
255
            if repo_path == '.':
 
256
                repo_path = ''
 
257
            if repo_path:
 
258
                repo_bzrdir_format = RemoteBzrDirFormat()
 
259
                repo_bzrdir_format._network_name = response[5]
 
260
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
 
261
                    repo_bzrdir_format)
 
262
            else:
 
263
                repo_bzr = bzrdir
 
264
            final_stack = response[8] or None
 
265
            final_stack_pwd = response[9] or None
 
266
            if final_stack_pwd:
 
267
                final_stack_pwd = urlutils.join(
 
268
                    transport.base, final_stack_pwd)
 
269
            remote_repo = RemoteRepository(repo_bzr, repo_format)
 
270
            if len(response) > 10:
 
271
                # Updated server verb that locks remotely.
 
272
                repo_lock_token = response[10] or None
 
273
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
 
274
                if repo_lock_token:
 
275
                    remote_repo.dont_leave_lock_in_place()
 
276
            else:
 
277
                remote_repo.lock_write()
 
278
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
 
279
                final_stack_pwd, require_stacking)
 
280
            policy.acquire_repository()
 
281
        else:
 
282
            remote_repo = None
 
283
            policy = None
 
284
        bzrdir._format.set_branch_format(self.get_branch_format())
 
285
        if require_stacking:
 
286
            # The repo has already been created, but we need to make sure that
 
287
            # we'll make a stackable branch.
 
288
            bzrdir._format.require_stacking(_skip_repo=True)
 
289
        return remote_repo, bzrdir, require_stacking, policy
 
290
 
 
291
    def _open(self, transport):
 
292
        return RemoteBzrDir(transport, self)
 
293
 
 
294
    def __eq__(self, other):
 
295
        if not isinstance(other, RemoteBzrDirFormat):
 
296
            return False
 
297
        return self.get_format_description() == other.get_format_description()
 
298
 
 
299
    def __return_repository_format(self):
 
300
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
 
301
        # repository format has been asked for, tell the RemoteRepositoryFormat
 
302
        # that it should use that for init() etc.
 
303
        result = RemoteRepositoryFormat()
 
304
        custom_format = getattr(self, '_repository_format', None)
 
305
        if custom_format:
 
306
            if isinstance(custom_format, RemoteRepositoryFormat):
 
307
                return custom_format
 
308
            else:
 
309
                # We will use the custom format to create repositories over the
 
310
                # wire; expose its details like rich_root_data for code to
 
311
                # query
 
312
                result._custom_format = custom_format
 
313
        return result
 
314
 
 
315
    def get_branch_format(self):
 
316
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
 
317
        if not isinstance(result, RemoteBranchFormat):
 
318
            new_result = RemoteBranchFormat()
 
319
            new_result._custom_format = result
 
320
            # cache the result
 
321
            self.set_branch_format(new_result)
 
322
            result = new_result
 
323
        return result
 
324
 
 
325
    repository_format = property(__return_repository_format,
 
326
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
 
327
 
 
328
 
 
329
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
93
330
    """Control directory on a remote server, accessed via bzr:// or similar."""
94
331
 
95
332
    def __init__(self, transport, format, _client=None, _force_probe=False):
98
335
        :param _client: Private parameter for testing. Disables probing and the
99
336
            use of a real bzrdir.
100
337
        """
101
 
        BzrDir.__init__(self, transport, format)
 
338
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
102
339
        # this object holds a delegated bzrdir that uses file-level operations
103
340
        # to talk to the other side
104
341
        self._real_bzrdir = None
164
401
                import traceback
165
402
                warning('VFS BzrDir access triggered\n%s',
166
403
                    ''.join(traceback.format_stack()))
167
 
            self._real_bzrdir = BzrDir.open_from_transport(
 
404
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
168
405
                self.root_transport, _server_formats=False)
169
406
            self._format._network_name = \
170
407
                self._real_bzrdir._format.network_name()
176
413
        # Prevent aliasing problems in the next_open_branch_result cache.
177
414
        # See create_branch for rationale.
178
415
        self._next_open_branch_result = None
179
 
        return BzrDir.break_lock(self)
 
416
        return _mod_bzrdir.BzrDir.break_lock(self)
180
417
 
181
418
    def _vfs_cloning_metadir(self, require_stacking=False):
182
419
        self._ensure_real()
213
450
        if len(branch_info) != 2:
214
451
            raise errors.UnexpectedSmartServerResponse(response)
215
452
        branch_ref, branch_name = branch_info
216
 
        format = bzrdir.network_format_registry.get(control_name)
 
453
        format = controldir.network_format_registry.get(control_name)
217
454
        if repo_name:
218
 
            format.repository_format = repository.network_format_registry.get(
 
455
            format.repository_format = _mod_repository.network_format_registry.get(
219
456
                repo_name)
220
457
        if branch_ref == 'ref':
221
458
            # XXX: we need possible_transports here to avoid reopening the
222
459
            # connection to the referenced location
223
 
            ref_bzrdir = BzrDir.open(branch_name)
 
460
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
224
461
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
225
462
            format.set_branch_format(branch_format)
226
463
        elif branch_ref == 'branch':
245
482
        self._ensure_real()
246
483
        self._real_bzrdir.destroy_repository()
247
484
 
248
 
    def create_branch(self, name=None):
 
485
    def create_branch(self, name=None, repository=None):
249
486
        # as per meta1 formats - just delegate to the format object which may
250
487
        # be parameterised.
251
488
        real_branch = self._format.get_branch_format().initialize(self,
252
 
            name=name)
 
489
            name=name, repository=repository)
253
490
        if not isinstance(real_branch, RemoteBranch):
254
 
            result = RemoteBranch(self, self.find_repository(), real_branch,
255
 
                                  name=name)
 
491
            if not isinstance(repository, RemoteRepository):
 
492
                raise AssertionError(
 
493
                    'need a RemoteRepository to use with RemoteBranch, got %r'
 
494
                    % (repository,))
 
495
            result = RemoteBranch(self, repository, real_branch, name=name)
256
496
        else:
257
497
            result = real_branch
258
498
        # BzrDir.clone_on_transport() uses the result of create_branch but does
270
510
        self._real_bzrdir.destroy_branch(name=name)
271
511
        self._next_open_branch_result = None
272
512
 
273
 
    def create_workingtree(self, revision_id=None, from_branch=None):
 
513
    def create_workingtree(self, revision_id=None, from_branch=None,
 
514
        accelerator_tree=None, hardlink=False):
274
515
        raise errors.NotLocalUrl(self.transport.base)
275
516
 
276
517
    def find_branch_format(self, name=None):
445
686
        """Upgrading of remote bzrdirs is not supported yet."""
446
687
        return False
447
688
 
448
 
    def needs_format_conversion(self, format=None):
 
689
    def needs_format_conversion(self, format):
449
690
        """Upgrading of remote bzrdirs is not supported yet."""
450
 
        if format is None:
451
 
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
452
 
                % 'needs_format_conversion(format=None)')
453
691
        return False
454
692
 
455
693
    def clone(self, url, revision_id=None, force_new_repo=False,
462
700
        return RemoteBzrDirConfig(self)
463
701
 
464
702
 
465
 
class RemoteRepositoryFormat(repository.RepositoryFormat):
 
703
class RemoteRepositoryFormat(_mod_repository.RepositoryFormat):
466
704
    """Format for repositories accessed over a _SmartClient.
467
705
 
468
706
    Instances of this repository are represented by RemoteRepository
483
721
    """
484
722
 
485
723
    _matchingbzrdir = RemoteBzrDirFormat()
 
724
    supports_full_versioned_files = True
 
725
    supports_leaving_lock = True
486
726
 
487
727
    def __init__(self):
488
 
        repository.RepositoryFormat.__init__(self)
 
728
        _mod_repository.RepositoryFormat.__init__(self)
489
729
        self._custom_format = None
490
730
        self._network_name = None
491
731
        self._creating_bzrdir = None
492
732
        self._supports_chks = None
493
733
        self._supports_external_lookups = None
494
734
        self._supports_tree_reference = None
 
735
        self._supports_funky_characters = None
495
736
        self._rich_root_data = None
496
737
 
497
738
    def __repr__(self):
526
767
        return self._supports_external_lookups
527
768
 
528
769
    @property
 
770
    def supports_funky_characters(self):
 
771
        if self._supports_funky_characters is None:
 
772
            self._ensure_real()
 
773
            self._supports_funky_characters = \
 
774
                self._custom_format.supports_funky_characters
 
775
        return self._supports_funky_characters
 
776
 
 
777
    @property
529
778
    def supports_tree_reference(self):
530
779
        if self._supports_tree_reference is None:
531
780
            self._ensure_real()
573
822
            network_name = self._network_name
574
823
        else:
575
824
            # Select the current bzrlib default and ask for that.
576
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
825
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
577
826
            reference_format = reference_bzrdir_format.repository_format
578
827
            network_name = reference_format.network_name()
579
828
        # 2) try direct creation via RPC
605
854
 
606
855
    def _ensure_real(self):
607
856
        if self._custom_format is None:
608
 
            self._custom_format = repository.network_format_registry.get(
 
857
            self._custom_format = _mod_repository.network_format_registry.get(
609
858
                self._network_name)
610
859
 
611
860
    @property
648
897
 
649
898
 
650
899
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
651
 
    bzrdir.ControlComponent):
 
900
    controldir.ControlComponent):
652
901
    """Repository accessed over rpc.
653
902
 
654
903
    For the moment most operations are performed using local transport-backed
707
956
        # transport, but I'm not sure it's worth making this method
708
957
        # optional -- mbp 2010-04-21
709
958
        return self.bzrdir.get_repository_transport(None)
710
 
        
 
959
 
711
960
    def __str__(self):
712
961
        return "%s(%s)" % (self.__class__.__name__, self.base)
713
962
 
847
1096
        """Private method for using with old (< 1.2) servers to fallback."""
848
1097
        if revision_id is None:
849
1098
            revision_id = ''
850
 
        elif revision.is_null(revision_id):
 
1099
        elif _mod_revision.is_null(revision_id):
851
1100
            return {}
852
1101
 
853
1102
        path = self.bzrdir._path_for_remote_call(self._client)
899
1148
    def _has_same_fallbacks(self, other_repo):
900
1149
        """Returns true if the repositories have the same fallbacks."""
901
1150
        # XXX: copied from Repository; it should be unified into a base class
902
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
 
1151
        # <https://bugs.launchpad.net/bzr/+bug/401622>
903
1152
        my_fb = self._fallback_repositories
904
1153
        other_fb = other_repo._fallback_repositories
905
1154
        if len(my_fb) != len(other_fb):
934
1183
        """See Repository.gather_stats()."""
935
1184
        path = self.bzrdir._path_for_remote_call(self._client)
936
1185
        # revid can be None to indicate no revisions, not just NULL_REVISION
937
 
        if revid is None or revision.is_null(revid):
 
1186
        if revid is None or _mod_revision.is_null(revid):
938
1187
            fmt_revid = ''
939
1188
        else:
940
1189
            fmt_revid = revid
1342
1591
        return result
1343
1592
 
1344
1593
    @needs_read_lock
1345
 
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
 
1594
    def search_missing_revision_ids(self, other,
 
1595
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
 
1596
            find_ghosts=True, revision_ids=None, if_present_ids=None):
1346
1597
        """Return the revision ids that other has that this does not.
1347
1598
 
1348
1599
        These are returned in topological order.
1349
1600
 
1350
1601
        revision_id: only return revision ids included by revision_id.
1351
1602
        """
1352
 
        return repository.InterRepository.get(
1353
 
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
 
1603
        if symbol_versioning.deprecated_passed(revision_id):
 
1604
            symbol_versioning.warn(
 
1605
                'search_missing_revision_ids(revision_id=...) was '
 
1606
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
 
1607
                DeprecationWarning, stacklevel=2)
 
1608
            if revision_ids is not None:
 
1609
                raise AssertionError(
 
1610
                    'revision_ids is mutually exclusive with revision_id')
 
1611
            if revision_id is not None:
 
1612
                revision_ids = [revision_id]
 
1613
        inter_repo = _mod_repository.InterRepository.get(other, self)
 
1614
        return inter_repo.search_missing_revision_ids(
 
1615
            find_ghosts=find_ghosts, revision_ids=revision_ids,
 
1616
            if_present_ids=if_present_ids)
1354
1617
 
1355
 
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
 
1618
    def fetch(self, source, revision_id=None, find_ghosts=False,
1356
1619
            fetch_spec=None):
1357
1620
        # No base implementation to use as RemoteRepository is not a subclass
1358
1621
        # of Repository; so this is a copy of Repository.fetch().
1369
1632
            # check that last_revision is in 'from' and then return a
1370
1633
            # no-operation.
1371
1634
            if (revision_id is not None and
1372
 
                not revision.is_null(revision_id)):
 
1635
                not _mod_revision.is_null(revision_id)):
1373
1636
                self.get_revision(revision_id)
1374
1637
            return 0, []
1375
1638
        # if there is no specific appropriate InterRepository, this will get
1376
1639
        # the InterRepository base class, which raises an
1377
1640
        # IncompatibleRepositories when asked to fetch.
1378
 
        inter = repository.InterRepository.get(source, self)
1379
 
        return inter.fetch(revision_id=revision_id, pb=pb,
 
1641
        inter = _mod_repository.InterRepository.get(source, self)
 
1642
        return inter.fetch(revision_id=revision_id,
1380
1643
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1381
1644
 
1382
1645
    def create_bundle(self, target, base, fileobj, format=None):
1606
1869
            tmpdir = osutils.mkdtemp()
1607
1870
            try:
1608
1871
                _extract_tar(tar, tmpdir)
1609
 
                tmp_bzrdir = BzrDir.open(tmpdir)
 
1872
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
1610
1873
                tmp_repo = tmp_bzrdir.open_repository()
1611
1874
                tmp_repo.copy_content_into(destination, revision_id)
1612
1875
            finally:
1757
2020
        return '\n'.join((start_keys, stop_keys, count))
1758
2021
 
1759
2022
    def _serialise_search_result(self, search_result):
1760
 
        if isinstance(search_result, graph.PendingAncestryResult):
1761
 
            parts = ['ancestry-of']
1762
 
            parts.extend(search_result.heads)
1763
 
        else:
1764
 
            recipe = search_result.get_recipe()
1765
 
            parts = [recipe[0], self._serialise_search_recipe(recipe)]
 
2023
        parts = search_result.get_network_struct()
1766
2024
        return '\n'.join(parts)
1767
2025
 
1768
2026
    def autopack(self):
1778
2036
            raise errors.UnexpectedSmartServerResponse(response)
1779
2037
 
1780
2038
 
1781
 
class RemoteStreamSink(repository.StreamSink):
 
2039
class RemoteStreamSink(_mod_repository.StreamSink):
1782
2040
 
1783
2041
    def _insert_real(self, stream, src_format, resume_tokens):
1784
2042
        self.target_repo._ensure_real()
1885
2143
        self._last_substream and self._last_stream so that the stream can be
1886
2144
        resumed by _resume_stream_with_vfs.
1887
2145
        """
1888
 
                    
 
2146
 
1889
2147
        stream_iter = iter(stream)
1890
2148
        for substream_kind, substream in stream_iter:
1891
2149
            if substream_kind == 'inventory-deltas':
1894
2152
                return
1895
2153
            else:
1896
2154
                yield substream_kind, substream
1897
 
            
1898
 
 
1899
 
class RemoteStreamSource(repository.StreamSource):
 
2155
 
 
2156
 
 
2157
class RemoteStreamSource(_mod_repository.StreamSource):
1900
2158
    """Stream data from a remote server."""
1901
2159
 
1902
2160
    def get_stream(self, search):
1962
2220
        candidate_verbs = [
1963
2221
            ('Repository.get_stream_1.19', (1, 19)),
1964
2222
            ('Repository.get_stream', (1, 13))]
 
2223
 
1965
2224
        found_verb = False
1966
2225
        for verb, version in candidate_verbs:
1967
2226
            if medium._is_remote_before(version):
1971
2230
                    verb, args, search_bytes)
1972
2231
            except errors.UnknownSmartMethod:
1973
2232
                medium._remember_remote_is_before(version)
 
2233
            except errors.UnknownErrorFromSmartServer, e:
 
2234
                if isinstance(search, graph.EverythingResult):
 
2235
                    error_verb = e.error_from_smart_server.error_verb
 
2236
                    if error_verb == 'BadSearch':
 
2237
                        # Pre-2.4 servers don't support this sort of search.
 
2238
                        # XXX: perhaps falling back to VFS on BadSearch is a
 
2239
                        # good idea in general?  It might provide a little bit
 
2240
                        # of protection against client-side bugs.
 
2241
                        medium._remember_remote_is_before((2, 4))
 
2242
                        break
 
2243
                raise
1974
2244
            else:
1975
2245
                response_tuple, response_handler = response
1976
2246
                found_verb = True
1980
2250
        if response_tuple[0] != 'ok':
1981
2251
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1982
2252
        byte_stream = response_handler.read_streamed_body()
1983
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
 
2253
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
 
2254
            self._record_counter)
1984
2255
        if src_format.network_name() != repo._format.network_name():
1985
2256
            raise AssertionError(
1986
2257
                "Mismatched RemoteRepository and stream src %r, %r" % (
2090
2361
                                  name=name)
2091
2362
        return result
2092
2363
 
2093
 
    def initialize(self, a_bzrdir, name=None):
 
2364
    def initialize(self, a_bzrdir, name=None, repository=None):
2094
2365
        # 1) get the network name to use.
2095
2366
        if self._custom_format:
2096
2367
            network_name = self._custom_format.network_name()
2097
2368
        else:
2098
2369
            # Select the current bzrlib default and ask for that.
2099
 
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
2370
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
2100
2371
            reference_format = reference_bzrdir_format.get_branch_format()
2101
2372
            self._custom_format = reference_format
2102
2373
            network_name = reference_format.network_name()
2124
2395
        # Turn the response into a RemoteRepository object.
2125
2396
        format = RemoteBranchFormat(network_name=response[1])
2126
2397
        repo_format = response_tuple_to_repo_format(response[3:])
2127
 
        if response[2] == '':
2128
 
            repo_bzrdir = a_bzrdir
 
2398
        repo_path = response[2]
 
2399
        if repository is not None:
 
2400
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
 
2401
            url_diff = urlutils.relative_url(repository.user_url,
 
2402
                    remote_repo_url)
 
2403
            if url_diff != '.':
 
2404
                raise AssertionError(
 
2405
                    'repository.user_url %r does not match URL from server '
 
2406
                    'response (%r + %r)'
 
2407
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
 
2408
            remote_repo = repository
2129
2409
        else:
2130
 
            repo_bzrdir = RemoteBzrDir(
2131
 
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2132
 
                a_bzrdir._client)
2133
 
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
2410
            if repo_path == '':
 
2411
                repo_bzrdir = a_bzrdir
 
2412
            else:
 
2413
                repo_bzrdir = RemoteBzrDir(
 
2414
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
 
2415
                    a_bzrdir._client)
 
2416
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2134
2417
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2135
2418
            format=format, setup_stacking=False, name=name)
2136
2419
        # XXX: We know this is a new branch, so it must have revno 0, revid
2157
2440
        self._ensure_real()
2158
2441
        return self._custom_format.supports_set_append_revisions_only()
2159
2442
 
 
2443
    def _use_default_local_heads_to_fetch(self):
 
2444
        # If the branch format is a metadir format *and* its heads_to_fetch
 
2445
        # implementation is not overridden vs the base class, we can use the
 
2446
        # base class logic rather than use the heads_to_fetch RPC.  This is
 
2447
        # usually cheaper in terms of net round trips, as the last-revision and
 
2448
        # tags info fetched is cached and would be fetched anyway.
 
2449
        self._ensure_real()
 
2450
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
 
2451
            branch_class = self._custom_format._branch_class()
 
2452
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
 
2453
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
 
2454
                return True
 
2455
        return False
2160
2456
 
2161
2457
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2162
2458
    """Branch stored on a server accessed by HPSS RPC.
2361
2657
            self._is_stacked = False
2362
2658
        else:
2363
2659
            self._is_stacked = True
2364
 
        
 
2660
 
2365
2661
    def _vfs_get_tags_bytes(self):
2366
2662
        self._ensure_real()
2367
2663
        return self._real_branch._get_tags_bytes()
2368
2664
 
 
2665
    @needs_read_lock
2369
2666
    def _get_tags_bytes(self):
 
2667
        if self._tags_bytes is None:
 
2668
            self._tags_bytes = self._get_tags_bytes_via_hpss()
 
2669
        return self._tags_bytes
 
2670
 
 
2671
    def _get_tags_bytes_via_hpss(self):
2370
2672
        medium = self._client._medium
2371
2673
        if medium._is_remote_before((1, 13)):
2372
2674
            return self._vfs_get_tags_bytes()
2382
2684
        return self._real_branch._set_tags_bytes(bytes)
2383
2685
 
2384
2686
    def _set_tags_bytes(self, bytes):
 
2687
        if self.is_locked():
 
2688
            self._tags_bytes = bytes
2385
2689
        medium = self._client._medium
2386
2690
        if medium._is_remote_before((1, 18)):
2387
2691
            self._vfs_set_tags_bytes(bytes)
2419
2723
            repo_token = self.repository.lock_write().repository_token
2420
2724
            self.repository.unlock()
2421
2725
        err_context = {'token': token}
2422
 
        response = self._call(
2423
 
            'Branch.lock_write', self._remote_path(), branch_token,
2424
 
            repo_token or '', **err_context)
 
2726
        try:
 
2727
            response = self._call(
 
2728
                'Branch.lock_write', self._remote_path(), branch_token,
 
2729
                repo_token or '', **err_context)
 
2730
        except errors.LockContention, e:
 
2731
            # The LockContention from the server doesn't have any
 
2732
            # information about the lock_url. We re-raise LockContention
 
2733
            # with valid lock_url.
 
2734
            raise errors.LockContention('(remote lock)',
 
2735
                self.repository.base.split('.bzr/')[0])
2425
2736
        if response[0] != 'ok':
2426
2737
            raise errors.UnexpectedSmartServerResponse(response)
2427
2738
        ok, branch_token, repo_token = response
2448
2759
            self._lock_mode = 'w'
2449
2760
            self._lock_count = 1
2450
2761
        elif self._lock_mode == 'r':
2451
 
            raise errors.ReadOnlyTransaction
 
2762
            raise errors.ReadOnlyError(self)
2452
2763
        else:
2453
2764
            if token is not None:
2454
2765
                # A token was given to lock_write, and we're relocking, so
2687
2998
        # XXX: These should be returned by the set_last_revision_info verb
2688
2999
        old_revno, old_revid = self.last_revision_info()
2689
3000
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2690
 
        revision_id = ensure_null(revision_id)
 
3001
        revision_id = _mod_revision.ensure_null(revision_id)
2691
3002
        try:
2692
3003
            response = self._call('Branch.set_last_revision_info',
2693
3004
                self._remote_path(), self._lock_token, self._repo_lock_token,
2729
3040
        self._ensure_real()
2730
3041
        return self._real_branch.set_push_location(location)
2731
3042
 
 
3043
    def heads_to_fetch(self):
 
3044
        if self._format._use_default_local_heads_to_fetch():
 
3045
            # We recognise this format, and its heads-to-fetch implementation
 
3046
            # is the default one (tip + tags).  In this case it's cheaper to
 
3047
            # just use the default implementation rather than a special RPC as
 
3048
            # the tip and tags data is cached.
 
3049
            return branch.Branch.heads_to_fetch(self)
 
3050
        medium = self._client._medium
 
3051
        if medium._is_remote_before((2, 4)):
 
3052
            return self._vfs_heads_to_fetch()
 
3053
        try:
 
3054
            return self._rpc_heads_to_fetch()
 
3055
        except errors.UnknownSmartMethod:
 
3056
            medium._remember_remote_is_before((2, 4))
 
3057
            return self._vfs_heads_to_fetch()
 
3058
 
 
3059
    def _rpc_heads_to_fetch(self):
 
3060
        response = self._call('Branch.heads_to_fetch', self._remote_path())
 
3061
        if len(response) != 2:
 
3062
            raise errors.UnexpectedSmartServerResponse(response)
 
3063
        must_fetch, if_present_fetch = response
 
3064
        return set(must_fetch), set(if_present_fetch)
 
3065
 
 
3066
    def _vfs_heads_to_fetch(self):
 
3067
        self._ensure_real()
 
3068
        return self._real_branch.heads_to_fetch()
 
3069
 
2732
3070
 
2733
3071
class RemoteConfig(object):
2734
3072
    """A Config that reads and writes from smart verbs.
2788
3126
        medium = self._branch._client._medium
2789
3127
        if medium._is_remote_before((1, 14)):
2790
3128
            return self._vfs_set_option(value, name, section)
 
3129
        if isinstance(value, dict):
 
3130
            if medium._is_remote_before((2, 2)):
 
3131
                return self._vfs_set_option(value, name, section)
 
3132
            return self._set_config_option_dict(value, name, section)
 
3133
        else:
 
3134
            return self._set_config_option(value, name, section)
 
3135
 
 
3136
    def _set_config_option(self, value, name, section):
2791
3137
        try:
2792
3138
            path = self._branch._remote_path()
2793
3139
            response = self._branch._client.call('Branch.set_config_option',
2794
3140
                path, self._branch._lock_token, self._branch._repo_lock_token,
2795
3141
                value.encode('utf8'), name, section or '')
2796
3142
        except errors.UnknownSmartMethod:
 
3143
            medium = self._branch._client._medium
2797
3144
            medium._remember_remote_is_before((1, 14))
2798
3145
            return self._vfs_set_option(value, name, section)
2799
3146
        if response != ():
2800
3147
            raise errors.UnexpectedSmartServerResponse(response)
2801
3148
 
 
3149
    def _serialize_option_dict(self, option_dict):
 
3150
        utf8_dict = {}
 
3151
        for key, value in option_dict.items():
 
3152
            if isinstance(key, unicode):
 
3153
                key = key.encode('utf8')
 
3154
            if isinstance(value, unicode):
 
3155
                value = value.encode('utf8')
 
3156
            utf8_dict[key] = value
 
3157
        return bencode.bencode(utf8_dict)
 
3158
 
 
3159
    def _set_config_option_dict(self, value, name, section):
 
3160
        try:
 
3161
            path = self._branch._remote_path()
 
3162
            serialised_dict = self._serialize_option_dict(value)
 
3163
            response = self._branch._client.call(
 
3164
                'Branch.set_config_option_dict',
 
3165
                path, self._branch._lock_token, self._branch._repo_lock_token,
 
3166
                serialised_dict, name, section or '')
 
3167
        except errors.UnknownSmartMethod:
 
3168
            medium = self._branch._client._medium
 
3169
            medium._remember_remote_is_before((2, 2))
 
3170
            return self._vfs_set_option(value, name, section)
 
3171
        if response != ():
 
3172
            raise errors.UnexpectedSmartServerResponse(response)
 
3173
 
2802
3174
    def _real_object(self):
2803
3175
        self._branch._ensure_real()
2804
3176
        return self._branch._real_branch
2887
3259
                    'Missing key %r in context %r', key_err.args[0], context)
2888
3260
                raise err
2889
3261
 
2890
 
    if err.error_verb == 'IncompatibleRepositories':
2891
 
        raise errors.IncompatibleRepositories(err.error_args[0],
2892
 
            err.error_args[1], err.error_args[2])
2893
 
    elif err.error_verb == 'NoSuchRevision':
 
3262
    if err.error_verb == 'NoSuchRevision':
2894
3263
        raise NoSuchRevision(find('branch'), err.error_args[0])
2895
3264
    elif err.error_verb == 'nosuchrevision':
2896
3265
        raise NoSuchRevision(find('repository'), err.error_args[0])
2903
3272
            detail=extra)
2904
3273
    elif err.error_verb == 'norepository':
2905
3274
        raise errors.NoRepositoryPresent(find('bzrdir'))
2906
 
    elif err.error_verb == 'LockContention':
2907
 
        raise errors.LockContention('(remote lock)')
2908
3275
    elif err.error_verb == 'UnlockableTransport':
2909
3276
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
2910
 
    elif err.error_verb == 'LockFailed':
2911
 
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
2912
3277
    elif err.error_verb == 'TokenMismatch':
2913
3278
        raise errors.TokenMismatch(find('token'), '(remote token)')
2914
3279
    elif err.error_verb == 'Diverged':
2915
3280
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
2916
 
    elif err.error_verb == 'TipChangeRejected':
2917
 
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2918
 
    elif err.error_verb == 'UnstackableBranchFormat':
2919
 
        raise errors.UnstackableBranchFormat(*err.error_args)
2920
 
    elif err.error_verb == 'UnstackableRepositoryFormat':
2921
 
        raise errors.UnstackableRepositoryFormat(*err.error_args)
2922
3281
    elif err.error_verb == 'NotStacked':
2923
3282
        raise errors.NotStacked(branch=find('branch'))
2924
3283
    elif err.error_verb == 'PermissionDenied':
2934
3293
    elif err.error_verb == 'NoSuchFile':
2935
3294
        path = get_path()
2936
3295
        raise errors.NoSuchFile(path)
 
3296
    _translate_error_without_context(err)
 
3297
 
 
3298
 
 
3299
def _translate_error_without_context(err):
 
3300
    """Translate any ErrorFromSmartServer values that don't require context"""
 
3301
    if err.error_verb == 'IncompatibleRepositories':
 
3302
        raise errors.IncompatibleRepositories(err.error_args[0],
 
3303
            err.error_args[1], err.error_args[2])
 
3304
    elif err.error_verb == 'LockContention':
 
3305
        raise errors.LockContention('(remote lock)')
 
3306
    elif err.error_verb == 'LockFailed':
 
3307
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
3308
    elif err.error_verb == 'TipChangeRejected':
 
3309
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
 
3310
    elif err.error_verb == 'UnstackableBranchFormat':
 
3311
        raise errors.UnstackableBranchFormat(*err.error_args)
 
3312
    elif err.error_verb == 'UnstackableRepositoryFormat':
 
3313
        raise errors.UnstackableRepositoryFormat(*err.error_args)
2937
3314
    elif err.error_verb == 'FileExists':
2938
3315
        raise errors.FileExists(err.error_args[0])
2939
3316
    elif err.error_verb == 'DirectoryNotEmpty':
2958
3335
            raise UnicodeEncodeError(encoding, val, start, end, reason)
2959
3336
    elif err.error_verb == 'ReadOnlyError':
2960
3337
        raise errors.TransportNotPossible('readonly transport')
 
3338
    elif err.error_verb == 'MemoryError':
 
3339
        raise errors.BzrError("remote server out of memory\n"
 
3340
            "Retry non-remotely, or contact the server admin for details.")
2961
3341
    raise errors.UnknownErrorFromSmartServer(err)