~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Martin Pool
  • Date: 2009-03-03 03:01:49 UTC
  • mfrom: (4070 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4073.
  • Revision ID: mbp@sourcefrog.net-20090303030149-8p8o8hszdtqa7w8f
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
from bzrlib import (
23
23
    branch,
 
24
    bzrdir,
24
25
    debug,
25
26
    errors,
26
27
    graph,
27
28
    lockdir,
 
29
    pack,
28
30
    repository,
29
31
    revision,
30
32
    symbol_versioning,
38
40
    SmartProtocolError,
39
41
    )
40
42
from bzrlib.lockable_files import LockableFiles
41
 
from bzrlib.smart import client, vfs
 
43
from bzrlib.smart import client, vfs, repository as smart_repo
42
44
from bzrlib.revision import ensure_null, NULL_REVISION
43
45
from bzrlib.trace import mutter, note, warning
 
46
from bzrlib.util import bencode
44
47
 
45
48
 
46
49
class _RpcHelper(object):
51
54
            return self._client.call(method, *args)
52
55
        except errors.ErrorFromSmartServer, err:
53
56
            self._translate_error(err, **err_context)
54
 
        
 
57
 
55
58
    def _call_expecting_body(self, method, *args, **err_context):
56
59
        try:
57
60
            return self._client.call_expecting_body(method, *args)
58
61
        except errors.ErrorFromSmartServer, err:
59
62
            self._translate_error(err, **err_context)
60
 
        
 
63
 
61
64
    def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
62
65
                                             **err_context):
63
66
        try:
65
68
                method, args, body_bytes)
66
69
        except errors.ErrorFromSmartServer, err:
67
70
            self._translate_error(err, **err_context)
68
 
        
 
71
 
 
72
 
 
73
def response_tuple_to_repo_format(response):
 
74
    """Convert a response tuple describing a repository format to a format."""
 
75
    format = RemoteRepositoryFormat()
 
76
    format.rich_root_data = (response[0] == 'yes')
 
77
    format.supports_tree_reference = (response[1] == 'yes')
 
78
    format.supports_external_lookups = (response[2] == 'yes')
 
79
    format._network_name = response[3]
 
80
    return format
 
81
 
 
82
 
69
83
# Note: RemoteBzrDirFormat is in bzrdir.py
70
84
 
71
85
class RemoteBzrDir(BzrDir, _RpcHelper):
81
95
        # this object holds a delegated bzrdir that uses file-level operations
82
96
        # to talk to the other side
83
97
        self._real_bzrdir = None
 
98
        # 1-shot cache for the call pattern 'create_branch; open_branch' - see
 
99
        # create_branch for details.
 
100
        self._next_open_branch_result = None
84
101
 
85
102
        if _client is None:
86
103
            medium = transport.get_smart_medium()
108
125
    def _translate_error(self, err, **context):
109
126
        _translate_error(err, bzrdir=self, **context)
110
127
 
 
128
    def break_lock(self):
 
129
        # Prevent aliasing problems in the next_open_branch_result cache.
 
130
        # See create_branch for rationale.
 
131
        self._next_open_branch_result = None
 
132
        return BzrDir.break_lock(self)
 
133
 
111
134
    def cloning_metadir(self, stacked=False):
112
135
        self._ensure_real()
113
136
        return self._real_bzrdir.cloning_metadir(stacked)
131
154
        # be parameterised.
132
155
        real_branch = self._format.get_branch_format().initialize(self)
133
156
        if not isinstance(real_branch, RemoteBranch):
134
 
            return RemoteBranch(self, self.find_repository(), real_branch)
 
157
            result = RemoteBranch(self, self.find_repository(), real_branch)
135
158
        else:
136
 
            return real_branch
 
159
            result = real_branch
 
160
        # BzrDir.clone_on_transport() uses the result of create_branch but does
 
161
        # not return it to its callers; we save approximately 8% of our round
 
162
        # trips by handing the branch we created back to the first caller to
 
163
        # open_branch rather than probing anew. Long term we need a API in
 
164
        # bzrdir that doesn't discard result objects (like result_branch).
 
165
        # RBC 20090225
 
166
        self._next_open_branch_result = result
 
167
        return result
137
168
 
138
169
    def destroy_branch(self):
139
170
        """See BzrDir.destroy_branch"""
140
171
        self._ensure_real()
141
172
        self._real_bzrdir.destroy_branch()
 
173
        self._next_open_branch_result = None
142
174
 
143
175
    def create_workingtree(self, revision_id=None, from_branch=None):
144
176
        raise errors.NotLocalUrl(self.transport.base)
172
204
    def open_branch(self, _unsupported=False):
173
205
        if _unsupported:
174
206
            raise NotImplementedError('unsupported flag support not implemented yet.')
 
207
        if self._next_open_branch_result is not None:
 
208
            # See create_branch for details.
 
209
            result = self._next_open_branch_result
 
210
            self._next_open_branch_result = None
 
211
            return result
175
212
        reference_url = self.get_branch_reference()
176
213
        if reference_url is None:
177
214
            # branch at this location.
180
217
            # a branch reference, use the existing BranchReference logic.
181
218
            format = BranchReferenceFormat()
182
219
            return format.open(self, _found=True, location=reference_url)
183
 
                
 
220
 
 
221
    def _open_repo_v1(self, path):
 
222
        verb = 'BzrDir.find_repository'
 
223
        response = self._call(verb, path)
 
224
        if response[0] != 'ok':
 
225
            raise errors.UnexpectedSmartServerResponse(response)
 
226
        # servers that only support the v1 method don't support external
 
227
        # references either.
 
228
        self._ensure_real()
 
229
        repo = self._real_bzrdir.open_repository()
 
230
        response = response + ('no', repo._format.network_name())
 
231
        return response, repo
 
232
 
 
233
    def _open_repo_v2(self, path):
 
234
        verb = 'BzrDir.find_repositoryV2'
 
235
        response = self._call(verb, path)
 
236
        if response[0] != 'ok':
 
237
            raise errors.UnexpectedSmartServerResponse(response)
 
238
        self._ensure_real()
 
239
        repo = self._real_bzrdir.open_repository()
 
240
        response = response + (repo._format.network_name(),)
 
241
        return response, repo
 
242
 
 
243
    def _open_repo_v3(self, path):
 
244
        verb = 'BzrDir.find_repositoryV3'
 
245
        medium = self._client._medium
 
246
        if medium._is_remote_before((1, 13)):
 
247
            raise errors.UnknownSmartMethod(verb)
 
248
        response = self._call(verb, path)
 
249
        if response[0] != 'ok':
 
250
            raise errors.UnexpectedSmartServerResponse(response)
 
251
        return response, None
 
252
 
184
253
    def open_repository(self):
185
254
        path = self._path_for_remote_call(self._client)
186
 
        verb = 'BzrDir.find_repositoryV2'
187
 
        try:
188
 
            response = self._call(verb, path)
189
 
        except errors.UnknownSmartMethod:
190
 
            verb = 'BzrDir.find_repository'
191
 
            response = self._call(verb, path)
 
255
        response = None
 
256
        for probe in [self._open_repo_v3, self._open_repo_v2,
 
257
            self._open_repo_v1]:
 
258
            try:
 
259
                response, real_repo = probe(path)
 
260
                break
 
261
            except errors.UnknownSmartMethod:
 
262
                pass
 
263
        if response is None:
 
264
            raise errors.UnknownSmartMethod('BzrDir.find_repository{3,2,}')
192
265
        if response[0] != 'ok':
193
266
            raise errors.UnexpectedSmartServerResponse(response)
194
 
        if verb == 'BzrDir.find_repository':
195
 
            # servers that don't support the V2 method don't support external
196
 
            # references either.
197
 
            response = response + ('no', )
198
 
        if not (len(response) == 5):
 
267
        if len(response) != 6:
199
268
            raise SmartProtocolError('incorrect response length %s' % (response,))
200
269
        if response[1] == '':
201
 
            format = RemoteRepositoryFormat()
202
 
            format.rich_root_data = (response[2] == 'yes')
203
 
            format.supports_tree_reference = (response[3] == 'yes')
204
 
            # No wire format to check this yet.
205
 
            format.supports_external_lookups = (response[4] == 'yes')
 
270
            # repo is at this dir.
 
271
            format = response_tuple_to_repo_format(response[2:])
206
272
            # Used to support creating a real format instance when needed.
207
273
            format._creating_bzrdir = self
208
 
            return RemoteRepository(self, format)
 
274
            remote_repo = RemoteRepository(self, format)
 
275
            format._creating_repo = remote_repo
 
276
            if real_repo is not None:
 
277
                remote_repo._set_real_repository(real_repo)
 
278
            return remote_repo
209
279
        else:
210
280
            raise errors.NoRepositoryPresent(self)
211
281
 
265
335
    the attributes rich_root_data and supports_tree_reference are set
266
336
    on a per instance basis, and are not set (and should not be) at
267
337
    the class level.
 
338
 
 
339
    :ivar _custom_format: If set, a specific concrete repository format that
 
340
        will be used when initializing a repository with this
 
341
        RemoteRepositoryFormat.
 
342
    :ivar _creating_repo: If set, the repository object that this
 
343
        RemoteRepositoryFormat was created for: it can be called into
 
344
        to obtain data like the network name.
268
345
    """
269
346
 
270
347
    _matchingbzrdir = RemoteBzrDirFormat()
272
349
    def __init__(self):
273
350
        repository.RepositoryFormat.__init__(self)
274
351
        self._custom_format = None
 
352
        self._network_name = None
 
353
        self._creating_bzrdir = None
275
354
 
276
 
    def initialize(self, a_bzrdir, shared=False):
 
355
    def _vfs_initialize(self, a_bzrdir, shared):
 
356
        """Helper for common code in initialize."""
277
357
        if self._custom_format:
278
 
            # This returns a custom instance - e.g. a pack repo, not a remote
279
 
            # repo.
280
 
            return self._custom_format.initialize(a_bzrdir, shared=shared)
281
 
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
358
            # Custom format requested
 
359
            result = self._custom_format.initialize(a_bzrdir, shared=shared)
 
360
        elif self._creating_bzrdir is not None:
 
361
            # Use the format that the repository we were created to back
 
362
            # has.
282
363
            prior_repo = self._creating_bzrdir.open_repository()
283
364
            prior_repo._ensure_real()
284
 
            return prior_repo._real_repository._format.initialize(
 
365
            result = prior_repo._real_repository._format.initialize(
285
366
                a_bzrdir, shared=shared)
286
 
        # delegate to a real object at this point (remoteBzrDir delegate to the
287
 
        # repository format which would lead to infinite recursion).
288
 
        a_bzrdir._ensure_real()
289
 
        result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
 
367
        else:
 
368
            # assume that a_bzr is a RemoteBzrDir but the smart server didn't
 
369
            # support remote initialization.
 
370
            # We delegate to a real object at this point (as RemoteBzrDir
 
371
            # delegate to the repository format which would lead to infinite
 
372
            # recursion if we just called a_bzrdir.create_repository.
 
373
            a_bzrdir._ensure_real()
 
374
            result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
290
375
        if not isinstance(result, RemoteRepository):
291
376
            return self.open(a_bzrdir)
292
377
        else:
293
378
            return result
294
 
    
 
379
 
 
380
    def initialize(self, a_bzrdir, shared=False):
 
381
        # Being asked to create on a non RemoteBzrDir:
 
382
        if not isinstance(a_bzrdir, RemoteBzrDir):
 
383
            return self._vfs_initialize(a_bzrdir, shared)
 
384
        medium = a_bzrdir._client._medium
 
385
        if medium._is_remote_before((1, 13)):
 
386
            return self._vfs_initialize(a_bzrdir, shared)
 
387
        # Creating on a remote bzr dir.
 
388
        # 1) get the network name to use.
 
389
        if self._custom_format:
 
390
            network_name = self._custom_format.network_name()
 
391
        else:
 
392
            # Select the current bzrlib default and ask for that.
 
393
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
394
            reference_format = reference_bzrdir_format.repository_format
 
395
            network_name = reference_format.network_name()
 
396
        # 2) try direct creation via RPC
 
397
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
398
        verb = 'BzrDir.create_repository'
 
399
        if shared:
 
400
            shared_str = 'True'
 
401
        else:
 
402
            shared_str = 'False'
 
403
        try:
 
404
            response = a_bzrdir._call(verb, path, network_name, shared_str)
 
405
        except errors.UnknownSmartMethod:
 
406
            # Fallback - use vfs methods
 
407
            return self._vfs_initialize(a_bzrdir, shared)
 
408
        else:
 
409
            # Turn the response into a RemoteRepository object.
 
410
            format = response_tuple_to_repo_format(response[1:])
 
411
            # Used to support creating a real format instance when needed.
 
412
            format._creating_bzrdir = a_bzrdir
 
413
            remote_repo = RemoteRepository(a_bzrdir, format)
 
414
            format._creating_repo = remote_repo
 
415
            return remote_repo
 
416
 
295
417
    def open(self, a_bzrdir):
296
418
        if not isinstance(a_bzrdir, RemoteBzrDir):
297
419
            raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
298
420
        return a_bzrdir.open_repository()
299
421
 
 
422
    def _ensure_real(self):
 
423
        if self._custom_format is None:
 
424
            self._custom_format = repository.network_format_registry.get(
 
425
                self._network_name)
 
426
 
 
427
    @property
 
428
    def _fetch_order(self):
 
429
        self._ensure_real()
 
430
        return self._custom_format._fetch_order
 
431
 
 
432
    @property
 
433
    def _fetch_uses_deltas(self):
 
434
        self._ensure_real()
 
435
        return self._custom_format._fetch_uses_deltas
 
436
 
 
437
    @property
 
438
    def _fetch_reconcile(self):
 
439
        self._ensure_real()
 
440
        return self._custom_format._fetch_reconcile
 
441
 
300
442
    def get_format_description(self):
301
443
        return 'bzr remote repository'
302
444
 
312
454
            raise errors.BadConversionTarget(
313
455
                'Does not support nested trees', target_format)
314
456
 
 
457
    def network_name(self):
 
458
        if self._network_name:
 
459
            return self._network_name
 
460
        self._creating_repo._ensure_real()
 
461
        return self._creating_repo._real_repository._format.network_name()
 
462
 
 
463
    @property
 
464
    def _serializer(self):
 
465
        self._ensure_real()
 
466
        return self._custom_format._serializer
 
467
 
315
468
 
316
469
class RemoteRepository(_RpcHelper):
317
470
    """Repository accessed over rpc.
322
475
 
323
476
    def __init__(self, remote_bzrdir, format, real_repository=None, _client=None):
324
477
        """Create a RemoteRepository instance.
325
 
        
 
478
 
326
479
        :param remote_bzrdir: The bzrdir hosting this repository.
327
480
        :param format: The RemoteFormat object to use.
328
481
        :param real_repository: If not None, a local implementation of the
367
520
 
368
521
    def abort_write_group(self, suppress_errors=False):
369
522
        """Complete a write group on the decorated repository.
370
 
        
 
523
 
371
524
        Smart methods peform operations in a single step so this api
372
525
        is not really applicable except as a compatibility thunk
373
526
        for older plugins that don't use e.g. the CommitBuilder
381
534
 
382
535
    def commit_write_group(self):
383
536
        """Complete a write group on the decorated repository.
384
 
        
 
537
 
385
538
        Smart methods peform operations in a single step so this api
386
539
        is not really applicable except as a compatibility thunk
387
540
        for older plugins that don't use e.g. the CommitBuilder
390
543
        self._ensure_real()
391
544
        return self._real_repository.commit_write_group()
392
545
 
 
546
    def resume_write_group(self, tokens):
 
547
        self._ensure_real()
 
548
        return self._real_repository.resume_write_group(tokens)
 
549
 
 
550
    def suspend_write_group(self):
 
551
        self._ensure_real()
 
552
        return self._real_repository.suspend_write_group()
 
553
 
393
554
    def _ensure_real(self):
394
555
        """Ensure that there is a _real_repository set.
395
556
 
455
616
        for line in lines:
456
617
            d = tuple(line.split())
457
618
            revision_graph[d[0]] = d[1:]
458
 
            
 
619
 
459
620
        return revision_graph
460
621
 
 
622
    def _get_sink(self):
 
623
        """See Repository._get_sink()."""
 
624
        return RemoteStreamSink(self)
 
625
 
 
626
    def _get_source(self, to_format):
 
627
        """Return a source for streaming from this repository."""
 
628
        return RemoteStreamSource(self, to_format)
 
629
 
461
630
    def has_revision(self, revision_id):
462
631
        """See Repository.has_revision()."""
463
632
        if revision_id == NULL_REVISION:
627
796
            implemented operations.
628
797
        """
629
798
        if self._real_repository is not None:
630
 
            raise AssertionError('_real_repository is already set')
 
799
            # Replacing an already set real repository.
 
800
            # We cannot do this [currently] if the repository is locked -
 
801
            # synchronised state might be lost.
 
802
            if self.is_locked():
 
803
                raise AssertionError('_real_repository is already set')
631
804
        if isinstance(repository, RemoteRepository):
632
805
            raise AssertionError()
633
806
        self._real_repository = repository
642
815
 
643
816
    def start_write_group(self):
644
817
        """Start a write group on the decorated repository.
645
 
        
 
818
 
646
819
        Smart methods peform operations in a single step so this api
647
820
        is not really applicable except as a compatibility thunk
648
821
        for older plugins that don't use e.g. the CommitBuilder
665
838
            raise errors.UnexpectedSmartServerResponse(response)
666
839
 
667
840
    def unlock(self):
 
841
        if not self._lock_count:
 
842
            raise errors.LockNotHeld(self)
668
843
        self._lock_count -= 1
669
844
        if self._lock_count > 0:
670
845
            return
697
872
 
698
873
    def _get_tarball(self, compression):
699
874
        """Return a TemporaryFile containing a repository tarball.
700
 
        
 
875
 
701
876
        Returns None if the server does not support sending tarballs.
702
877
        """
703
878
        import tempfile
749
924
 
750
925
    def add_fallback_repository(self, repository):
751
926
        """Add a repository to use for looking up data not held locally.
752
 
        
 
927
 
753
928
        :param repository: A repository.
754
929
        """
755
930
        # XXX: At the moment the RemoteRepository will allow fallbacks
760
935
        #
761
936
        # We need to accumulate additional repositories here, to pass them in
762
937
        # on various RPC's.
 
938
        #
763
939
        self._fallback_repositories.append(repository)
764
 
        # They are also seen by the fallback repository.  If it doesn't exist
765
 
        # yet they'll be added then.  This implicitly copies them.
766
 
        self._ensure_real()
 
940
        # If self._real_repository was parameterised already (e.g. because a
 
941
        # _real_branch had its get_stacked_on_url method called), then the
 
942
        # repository to be added may already be in the _real_repositories list.
 
943
        if self._real_repository is not None:
 
944
            if repository not in self._real_repository._fallback_repositories:
 
945
                self._real_repository.add_fallback_repository(repository)
 
946
        else:
 
947
            # They are also seen by the fallback repository.  If it doesn't
 
948
            # exist yet they'll be added then.  This implicitly copies them.
 
949
            self._ensure_real()
767
950
 
768
951
    def add_inventory(self, revid, inv, parents):
769
952
        self._ensure_real()
824
1007
    @needs_read_lock
825
1008
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
826
1009
        """Return the revision ids that other has that this does not.
827
 
        
 
1010
 
828
1011
        These are returned in topological order.
829
1012
 
830
1013
        revision_id: only return revision ids included by revision_id.
865
1048
        self._ensure_real()
866
1049
        return self._real_repository._get_versioned_file_checker(
867
1050
            revisions, revision_versions_cache)
868
 
        
 
1051
 
869
1052
    def iter_files_bytes(self, desired_files):
870
1053
        """See Repository.iter_file_bytes.
871
1054
        """
872
1055
        self._ensure_real()
873
1056
        return self._real_repository.iter_files_bytes(desired_files)
874
1057
 
875
 
    @property
876
 
    def _fetch_order(self):
877
 
        """Decorate the real repository for now.
878
 
 
879
 
        In the long term getting this back from the remote repository as part
880
 
        of open would be more efficient.
881
 
        """
882
 
        self._ensure_real()
883
 
        return self._real_repository._fetch_order
884
 
 
885
 
    @property
886
 
    def _fetch_uses_deltas(self):
887
 
        """Decorate the real repository for now.
888
 
 
889
 
        In the long term getting this back from the remote repository as part
890
 
        of open would be more efficient.
891
 
        """
892
 
        self._ensure_real()
893
 
        return self._real_repository._fetch_uses_deltas
894
 
 
895
 
    @property
896
 
    def _fetch_reconcile(self):
897
 
        """Decorate the real repository for now.
898
 
 
899
 
        In the long term getting this back from the remote repository as part
900
 
        of open would be more efficient.
901
 
        """
902
 
        self._ensure_real()
903
 
        return self._real_repository._fetch_reconcile
904
 
 
905
1058
    def get_parent_map(self, revision_ids):
906
1059
        """See bzrlib.Graph.get_parent_map()."""
907
1060
        return self._make_parents_provider().get_parent_map(revision_ids)
1034
1187
    def reconcile(self, other=None, thorough=False):
1035
1188
        self._ensure_real()
1036
1189
        return self._real_repository.reconcile(other=other, thorough=thorough)
1037
 
        
 
1190
 
1038
1191
    def all_revision_ids(self):
1039
1192
        self._ensure_real()
1040
1193
        return self._real_repository.all_revision_ids()
1041
 
    
 
1194
 
1042
1195
    @needs_read_lock
1043
1196
    def get_deltas_for_revisions(self, revisions):
1044
1197
        self._ensure_real()
1129
1282
        return self._real_repository.revisions
1130
1283
 
1131
1284
    def set_make_working_trees(self, new_value):
1132
 
        self._ensure_real()
1133
 
        self._real_repository.set_make_working_trees(new_value)
 
1285
        if new_value:
 
1286
            new_value_str = "True"
 
1287
        else:
 
1288
            new_value_str = "False"
 
1289
        path = self.bzrdir._path_for_remote_call(self._client)
 
1290
        try:
 
1291
            response = self._call(
 
1292
                'Repository.set_make_working_trees', path, new_value_str)
 
1293
        except errors.UnknownSmartMethod:
 
1294
            self._ensure_real()
 
1295
            self._real_repository.set_make_working_trees(new_value)
 
1296
        else:
 
1297
            if response[0] != 'ok':
 
1298
                raise errors.UnexpectedSmartServerResponse(response)
1134
1299
 
1135
1300
    @property
1136
1301
    def signatures(self):
1163
1328
        return self._real_repository.get_revisions(revision_ids)
1164
1329
 
1165
1330
    def supports_rich_root(self):
1166
 
        self._ensure_real()
1167
 
        return self._real_repository.supports_rich_root()
 
1331
        return self._format.rich_root_data
1168
1332
 
1169
1333
    def iter_reverse_revision_history(self, revision_id):
1170
1334
        self._ensure_real()
1172
1336
 
1173
1337
    @property
1174
1338
    def _serializer(self):
1175
 
        self._ensure_real()
1176
 
        return self._real_repository._serializer
 
1339
        return self._format._serializer
1177
1340
 
1178
1341
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1179
1342
        self._ensure_real()
1243
1406
            raise errors.UnexpectedSmartServerResponse(response)
1244
1407
 
1245
1408
 
 
1409
class RemoteStreamSink(repository.StreamSink):
 
1410
 
 
1411
    def _insert_real(self, stream, src_format, resume_tokens):
 
1412
        self.target_repo._ensure_real()
 
1413
        sink = self.target_repo._real_repository._get_sink()
 
1414
        result = sink.insert_stream(stream, src_format, resume_tokens)
 
1415
        if not result:
 
1416
            self.target_repo.autopack()
 
1417
        return result
 
1418
 
 
1419
    def insert_stream(self, stream, src_format, resume_tokens):
 
1420
        repo = self.target_repo
 
1421
        client = repo._client
 
1422
        medium = client._medium
 
1423
        if medium._is_remote_before((1, 13)):
 
1424
            # No possible way this can work.
 
1425
            return self._insert_real(stream, src_format, resume_tokens)
 
1426
        path = repo.bzrdir._path_for_remote_call(client)
 
1427
        if not resume_tokens:
 
1428
            # XXX: Ugly but important for correctness, *will* be fixed during
 
1429
            # 1.13 cycle. Pushing a stream that is interrupted results in a
 
1430
            # fallback to the _real_repositories sink *with a partial stream*.
 
1431
            # Thats bad because we insert less data than bzr expected. To avoid
 
1432
            # this we do a trial push to make sure the verb is accessible, and
 
1433
            # do not fallback when actually pushing the stream. A cleanup patch
 
1434
            # is going to look at rewinding/restarting the stream/partial
 
1435
            # buffering etc.
 
1436
            byte_stream = smart_repo._stream_to_byte_stream([], src_format)
 
1437
            try:
 
1438
                response = client.call_with_body_stream(
 
1439
                    ('Repository.insert_stream', path, ''), byte_stream)
 
1440
            except errors.UnknownSmartMethod:
 
1441
                medium._remember_remote_is_before((1,13))
 
1442
                return self._insert_real(stream, src_format, resume_tokens)
 
1443
        byte_stream = smart_repo._stream_to_byte_stream(
 
1444
            stream, src_format)
 
1445
        resume_tokens = ' '.join(resume_tokens)
 
1446
        response = client.call_with_body_stream(
 
1447
            ('Repository.insert_stream', path, resume_tokens), byte_stream)
 
1448
        if response[0][0] not in ('ok', 'missing-basis'):
 
1449
            raise errors.UnexpectedSmartServerResponse(response)
 
1450
        if response[0][0] == 'missing-basis':
 
1451
            tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
 
1452
            resume_tokens = tokens
 
1453
            return resume_tokens, missing_keys
 
1454
        else:
 
1455
            if self.target_repo._real_repository is not None:
 
1456
                collection = getattr(self.target_repo._real_repository,
 
1457
                    '_pack_collection', None)
 
1458
                if collection is not None:
 
1459
                    collection.reload_pack_names()
 
1460
            return [], set()
 
1461
 
 
1462
 
 
1463
class RemoteStreamSource(repository.StreamSource):
 
1464
    """Stream data from a remote server."""
 
1465
 
 
1466
    def get_stream(self, search):
 
1467
        # streaming with fallback repositories is not well defined yet: The
 
1468
        # remote repository cannot see the fallback repositories, and thus
 
1469
        # cannot satisfy the entire search in the general case. Likewise the
 
1470
        # fallback repositories cannot reify the search to determine what they
 
1471
        # should send. It likely needs a return value in the stream listing the
 
1472
        # edge of the search to resume from in fallback repositories.
 
1473
        if self.from_repository._fallback_repositories:
 
1474
            return repository.StreamSource.get_stream(self, search)
 
1475
        repo = self.from_repository
 
1476
        client = repo._client
 
1477
        medium = client._medium
 
1478
        if medium._is_remote_before((1, 13)):
 
1479
            # No possible way this can work.
 
1480
            return repository.StreamSource.get_stream(self, search)
 
1481
        path = repo.bzrdir._path_for_remote_call(client)
 
1482
        try:
 
1483
            recipe = repo._serialise_search_recipe(search._recipe)
 
1484
            response = repo._call_with_body_bytes_expecting_body(
 
1485
                'Repository.get_stream',
 
1486
                (path, self.to_format.network_name()), recipe)
 
1487
            response_tuple, response_handler = response
 
1488
        except errors.UnknownSmartMethod:
 
1489
            medium._remember_remote_is_before((1,13))
 
1490
            return repository.StreamSource.get_stream(self, search)
 
1491
        if response_tuple[0] != 'ok':
 
1492
            raise errors.UnexpectedSmartServerResponse(response_tuple)
 
1493
        byte_stream = response_handler.read_streamed_body()
 
1494
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
 
1495
        if src_format.network_name() != repo._format.network_name():
 
1496
            raise AssertionError(
 
1497
                "Mismatched RemoteRepository and stream src %r, %r" % (
 
1498
                src_format.network_name(), repo._format.network_name()))
 
1499
        return stream
 
1500
 
 
1501
 
1246
1502
class RemoteBranchLockableFiles(LockableFiles):
1247
1503
    """A 'LockableFiles' implementation that talks to a smart server.
1248
 
    
 
1504
 
1249
1505
    This is not a public interface class.
1250
1506
    """
1251
1507
 
1269
1525
        super(RemoteBranchFormat, self).__init__()
1270
1526
        self._matchingbzrdir = RemoteBzrDirFormat()
1271
1527
        self._matchingbzrdir.set_branch_format(self)
 
1528
        self._custom_format = None
1272
1529
 
1273
1530
    def __eq__(self, other):
1274
 
        return (isinstance(other, RemoteBranchFormat) and 
 
1531
        return (isinstance(other, RemoteBranchFormat) and
1275
1532
            self.__dict__ == other.__dict__)
1276
1533
 
1277
1534
    def get_format_description(self):
1278
1535
        return 'Remote BZR Branch'
1279
1536
 
1280
 
    def get_format_string(self):
1281
 
        return 'Remote BZR Branch'
 
1537
    def network_name(self):
 
1538
        return self._network_name
1282
1539
 
1283
1540
    def open(self, a_bzrdir):
1284
1541
        return a_bzrdir.open_branch()
1285
1542
 
 
1543
    def _vfs_initialize(self, a_bzrdir):
 
1544
        # Initialisation when using a local bzrdir object, or a non-vfs init
 
1545
        # method is not available on the server.
 
1546
        # self._custom_format is always set - the start of initialize ensures
 
1547
        # that.
 
1548
        if isinstance(a_bzrdir, RemoteBzrDir):
 
1549
            a_bzrdir._ensure_real()
 
1550
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
 
1551
        else:
 
1552
            # We assume the bzrdir is parameterised; it may not be.
 
1553
            result = self._custom_format.initialize(a_bzrdir)
 
1554
        if (isinstance(a_bzrdir, RemoteBzrDir) and
 
1555
            not isinstance(result, RemoteBranch)):
 
1556
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
 
1557
        return result
 
1558
 
1286
1559
    def initialize(self, a_bzrdir):
1287
 
        # Delegate to a _real object here - the RemoteBzrDir format now
1288
 
        # supports delegating to parameterised branch formats and as such
1289
 
        # this RemoteBranchFormat method is only called when no specific format
1290
 
        # is selected.
 
1560
        # 1) get the network name to use.
 
1561
        if self._custom_format:
 
1562
            network_name = self._custom_format.network_name()
 
1563
        else:
 
1564
            # Select the current bzrlib default and ask for that.
 
1565
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
 
1566
            reference_format = reference_bzrdir_format.get_branch_format()
 
1567
            self._custom_format = reference_format
 
1568
            network_name = reference_format.network_name()
 
1569
        # Being asked to create on a non RemoteBzrDir:
1291
1570
        if not isinstance(a_bzrdir, RemoteBzrDir):
1292
 
            result = a_bzrdir.create_branch()
 
1571
            return self._vfs_initialize(a_bzrdir)
 
1572
        medium = a_bzrdir._client._medium
 
1573
        if medium._is_remote_before((1, 13)):
 
1574
            return self._vfs_initialize(a_bzrdir)
 
1575
        # Creating on a remote bzr dir.
 
1576
        # 2) try direct creation via RPC
 
1577
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
 
1578
        verb = 'BzrDir.create_branch'
 
1579
        try:
 
1580
            response = a_bzrdir._call(verb, path, network_name)
 
1581
        except errors.UnknownSmartMethod:
 
1582
            # Fallback - use vfs methods
 
1583
            return self._vfs_initialize(a_bzrdir)
 
1584
        if response[0] != 'ok':
 
1585
            raise errors.UnexpectedSmartServerResponse(response)
 
1586
        # Turn the response into a RemoteRepository object.
 
1587
        format = RemoteBranchFormat()
 
1588
        format._network_name = response[1]
 
1589
        repo_format = response_tuple_to_repo_format(response[3:])
 
1590
        if response[2] == '':
 
1591
            repo_bzrdir = a_bzrdir
1293
1592
        else:
1294
 
            a_bzrdir._ensure_real()
1295
 
            result = a_bzrdir._real_bzrdir.create_branch()
1296
 
        if not isinstance(result, RemoteBranch):
1297
 
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
1298
 
        return result
 
1593
            repo_bzrdir = RemoteBzrDir(
 
1594
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
 
1595
                a_bzrdir._client)
 
1596
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
1597
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
 
1598
            format=format, setup_stacking=False)
 
1599
        # XXX: We know this is a new branch, so it must have revno 0, revid
 
1600
        # NULL_REVISION. Creating the branch locked would make this be unable
 
1601
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
 
1602
        remote_branch._last_revision_info_cache = 0, NULL_REVISION
 
1603
        return remote_branch
1299
1604
 
1300
1605
    def supports_tags(self):
1301
1606
        # Remote branches might support tags, but we won't know until we
1310
1615
    """
1311
1616
 
1312
1617
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
1313
 
        _client=None):
 
1618
        _client=None, format=None, setup_stacking=True):
1314
1619
        """Create a RemoteBranch instance.
1315
1620
 
1316
1621
        :param real_branch: An optional local implementation of the branch
1317
1622
            format, usually accessing the data via the VFS.
1318
1623
        :param _client: Private parameter for testing.
 
1624
        :param format: A RemoteBranchFormat object, None to create one
 
1625
            automatically. If supplied it should have a network_name already
 
1626
            supplied.
 
1627
        :param setup_stacking: If True make an RPC call to determine the
 
1628
            stacked (or not) status of the branch. If False assume the branch
 
1629
            is not stacked.
1319
1630
        """
1320
1631
        # We intentionally don't call the parent class's __init__, because it
1321
1632
        # will try to assign to self.tags, which is a property in this subclass.
1344
1655
        else:
1345
1656
            self._real_branch = None
1346
1657
        # Fill out expected attributes of branch for bzrlib api users.
1347
 
        self._format = RemoteBranchFormat()
1348
1658
        self.base = self.bzrdir.root_transport.base
1349
1659
        self._control_files = None
1350
1660
        self._lock_mode = None
1352
1662
        self._repo_lock_token = None
1353
1663
        self._lock_count = 0
1354
1664
        self._leave_lock = False
 
1665
        # Setup a format: note that we cannot call _ensure_real until all the
 
1666
        # attributes above are set: This code cannot be moved higher up in this
 
1667
        # function.
 
1668
        if format is None:
 
1669
            self._format = RemoteBranchFormat()
 
1670
            if real_branch is not None:
 
1671
                self._format._network_name = \
 
1672
                    self._real_branch._format.network_name()
 
1673
            #else:
 
1674
            #    # XXX: Need to get this from BzrDir.open_branch's return value.
 
1675
            #    self._ensure_real()
 
1676
            #    self._format._network_name = \
 
1677
            #        self._real_branch._format.network_name()
 
1678
        else:
 
1679
            self._format = format
1355
1680
        # The base class init is not called, so we duplicate this:
1356
1681
        hooks = branch.Branch.hooks['open']
1357
1682
        for hook in hooks:
1358
1683
            hook(self)
1359
 
        self._setup_stacking()
 
1684
        if setup_stacking:
 
1685
            self._setup_stacking()
1360
1686
 
1361
1687
    def _setup_stacking(self):
1362
1688
        # configure stacking into the remote repository, by reading it from
1370
1696
        fallback_url = urlutils.join(self.base, fallback_url)
1371
1697
        transports = [self.bzrdir.root_transport]
1372
1698
        if self._real_branch is not None:
 
1699
            # The real repository is setup already:
1373
1700
            transports.append(self._real_branch._transport)
1374
 
        stacked_on = branch.Branch.open(fallback_url,
1375
 
                                        possible_transports=transports)
1376
 
        self.repository.add_fallback_repository(stacked_on.repository)
 
1701
            self.repository.add_fallback_repository(
 
1702
                self.repository._real_repository._fallback_repositories[0])
 
1703
        else:
 
1704
            stacked_on = branch.Branch.open(fallback_url,
 
1705
                                            possible_transports=transports)
 
1706
            self.repository.add_fallback_repository(stacked_on.repository)
1377
1707
 
1378
1708
    def _get_real_transport(self):
1379
1709
        # if we try vfs access, return the real branch's vfs transport
1432
1762
        too, in fact doing so might harm performance.
1433
1763
        """
1434
1764
        super(RemoteBranch, self)._clear_cached_state()
1435
 
        
 
1765
 
1436
1766
    @property
1437
1767
    def control_files(self):
1438
1768
        # Defer actually creating RemoteBranchLockableFiles until its needed,
1502
1832
            raise errors.UnexpectedSmartServerResponse(response)
1503
1833
        ok, branch_token, repo_token = response
1504
1834
        return branch_token, repo_token
1505
 
            
 
1835
 
1506
1836
    def lock_write(self, token=None):
1507
1837
        if not self._lock_mode:
1508
1838
            # Lock the branch and repo in one remote call.
1678
2008
        # Used by tests, when checking normalisation of given vs stored paths.
1679
2009
        self._ensure_real()
1680
2010
        return self._real_branch._get_parent_location()
1681
 
        
 
2011
 
1682
2012
    def set_parent(self, url):
1683
2013
        self._ensure_real()
1684
2014
        return self._real_branch.set_parent(url)
1690
2020
        else:
1691
2021
            self._ensure_real()
1692
2022
            return self._real_branch._set_parent_location(url)
1693
 
        
 
2023
 
1694
2024
    def set_stacked_on_url(self, stacked_location):
1695
2025
        """Set the URL this branch is stacked against.
1696
2026
 
1702
2032
        self._ensure_real()
1703
2033
        return self._real_branch.set_stacked_on_url(stacked_location)
1704
2034
 
1705
 
    def sprout(self, to_bzrdir, revision_id=None):
1706
 
        branch_format = to_bzrdir._format._branch_format
1707
 
        if (branch_format is None or
1708
 
            isinstance(branch_format, RemoteBranchFormat)):
1709
 
            # The to_bzrdir specifies RemoteBranchFormat (or no format, which
1710
 
            # implies the same thing), but RemoteBranches can't be created at
1711
 
            # arbitrary URLs.  So create a branch in the same format as
1712
 
            # _real_branch instead.
1713
 
            # XXX: if to_bzrdir is a RemoteBzrDir, this should perhaps do
1714
 
            # to_bzrdir.create_branch to create a RemoteBranch after all...
1715
 
            self._ensure_real()
1716
 
            result = self._real_branch._format.initialize(to_bzrdir)
1717
 
            self.copy_content_into(result, revision_id=revision_id)
1718
 
            result.set_parent(self.bzrdir.root_transport.base)
1719
 
        else:
1720
 
            result = branch.Branch.sprout(
1721
 
                self, to_bzrdir, revision_id=revision_id)
1722
 
        return result
1723
 
 
1724
2035
    @needs_write_lock
1725
2036
    def pull(self, source, overwrite=False, stop_revision=None,
1726
2037
             **kwargs):
1797
2108
        self._ensure_real()
1798
2109
        return self._real_branch.set_push_location(location)
1799
2110
 
1800
 
    @needs_write_lock
1801
 
    def update_revisions(self, other, stop_revision=None, overwrite=False,
1802
 
                         graph=None):
1803
 
        """See Branch.update_revisions."""
1804
 
        other.lock_read()
1805
 
        try:
1806
 
            if stop_revision is None:
1807
 
                stop_revision = other.last_revision()
1808
 
                if revision.is_null(stop_revision):
1809
 
                    # if there are no commits, we're done.
1810
 
                    return
1811
 
            self.fetch(other, stop_revision)
1812
 
 
1813
 
            if overwrite:
1814
 
                # Just unconditionally set the new revision.  We don't care if
1815
 
                # the branches have diverged.
1816
 
                self._set_last_revision(stop_revision)
1817
 
            else:
1818
 
                medium = self._client._medium
1819
 
                if not medium._is_remote_before((1, 6)):
1820
 
                    try:
1821
 
                        self._set_last_revision_descendant(stop_revision, other)
1822
 
                        return
1823
 
                    except errors.UnknownSmartMethod:
1824
 
                        medium._remember_remote_is_before((1, 6))
1825
 
                # Fallback for pre-1.6 servers: check for divergence
1826
 
                # client-side, then do _set_last_revision.
1827
 
                last_rev = revision.ensure_null(self.last_revision())
1828
 
                if graph is None:
1829
 
                    graph = self.repository.get_graph()
1830
 
                if self._check_if_descendant_or_diverged(
1831
 
                        stop_revision, last_rev, graph, other):
1832
 
                    # stop_revision is a descendant of last_rev, but we aren't
1833
 
                    # overwriting, so we're done.
1834
 
                    return
1835
 
                self._set_last_revision(stop_revision)
1836
 
        finally:
1837
 
            other.unlock()
1838
 
 
1839
2111
 
1840
2112
def _extract_tar(tar, to_dir):
1841
2113
    """Extract all the contents of a tarfile object.