37
39
SmartProtocolError,
39
41
from bzrlib.lockable_files import LockableFiles
40
from bzrlib.smart import client, vfs
42
from bzrlib.smart import client, vfs, repository as smart_repo
41
43
from bzrlib.revision import ensure_null, NULL_REVISION
42
44
from bzrlib.trace import mutter, note, warning
47
class _RpcHelper(object):
48
"""Mixin class that helps with issuing RPCs."""
50
def _call(self, method, *args, **err_context):
52
return self._client.call(method, *args)
53
except errors.ErrorFromSmartServer, err:
54
self._translate_error(err, **err_context)
56
def _call_expecting_body(self, method, *args, **err_context):
58
return self._client.call_expecting_body(method, *args)
59
except errors.ErrorFromSmartServer, err:
60
self._translate_error(err, **err_context)
62
def _call_with_body_bytes(self, method, args, body_bytes, **err_context):
64
return self._client.call_with_body_bytes(method, args, body_bytes)
65
except errors.ErrorFromSmartServer, err:
66
self._translate_error(err, **err_context)
68
def _call_with_body_bytes_expecting_body(self, method, args, body_bytes,
71
return self._client.call_with_body_bytes_expecting_body(
72
method, args, body_bytes)
73
except errors.ErrorFromSmartServer, err:
74
self._translate_error(err, **err_context)
77
def response_tuple_to_repo_format(response):
78
"""Convert a response tuple describing a repository format to a format."""
79
format = RemoteRepositoryFormat()
80
format._rich_root_data = (response[0] == 'yes')
81
format._supports_tree_reference = (response[1] == 'yes')
82
format._supports_external_lookups = (response[2] == 'yes')
83
format._network_name = response[3]
45
87
# Note: RemoteBzrDirFormat is in bzrdir.py
47
class RemoteBzrDir(BzrDir):
89
class RemoteBzrDir(BzrDir, _RpcHelper):
48
90
"""Control directory on a remote server, accessed via bzr:// or similar."""
50
def __init__(self, transport, _client=None):
92
def __init__(self, transport, format, _client=None):
51
93
"""Construct a RemoteBzrDir.
53
95
:param _client: Private parameter for testing. Disables probing and the
54
96
use of a real bzrdir.
56
BzrDir.__init__(self, transport, RemoteBzrDirFormat())
98
BzrDir.__init__(self, transport, format)
57
99
# this object holds a delegated bzrdir that uses file-level operations
58
100
# to talk to the other side
59
101
self._real_bzrdir = None
102
# 1-shot cache for the call pattern 'create_branch; open_branch' - see
103
# create_branch for details.
104
self._next_open_branch_result = None
61
106
if _client is None:
62
107
medium = transport.get_smart_medium()
80
125
if not self._real_bzrdir:
81
126
self._real_bzrdir = BzrDir.open_from_transport(
82
127
self.root_transport, _server_formats=False)
84
def cloning_metadir(self, stacked=False):
86
return self._real_bzrdir.cloning_metadir(stacked)
128
self._format._network_name = \
129
self._real_bzrdir._format.network_name()
88
131
def _translate_error(self, err, **context):
89
132
_translate_error(err, bzrdir=self, **context)
134
def break_lock(self):
135
# Prevent aliasing problems in the next_open_branch_result cache.
136
# See create_branch for rationale.
137
self._next_open_branch_result = None
138
return BzrDir.break_lock(self)
140
def _vfs_cloning_metadir(self, require_stacking=False):
142
return self._real_bzrdir.cloning_metadir(
143
require_stacking=require_stacking)
145
def cloning_metadir(self, require_stacking=False):
146
medium = self._client._medium
147
if medium._is_remote_before((1, 13)):
148
return self._vfs_cloning_metadir(require_stacking=require_stacking)
149
verb = 'BzrDir.cloning_metadir'
154
path = self._path_for_remote_call(self._client)
156
response = self._call(verb, path, stacking)
157
except errors.UnknownSmartMethod:
158
medium._remember_remote_is_before((1, 13))
159
return self._vfs_cloning_metadir(require_stacking=require_stacking)
160
except errors.UnknownErrorFromSmartServer, err:
161
if err.error_tuple != ('BranchReference',):
163
# We need to resolve the branch reference to determine the
164
# cloning_metadir. This causes unnecessary RPCs to open the
165
# referenced branch (and bzrdir, etc) but only when the caller
166
# didn't already resolve the branch reference.
167
referenced_branch = self.open_branch()
168
return referenced_branch.bzrdir.cloning_metadir()
169
if len(response) != 3:
170
raise errors.UnexpectedSmartServerResponse(response)
171
control_name, repo_name, branch_info = response
172
if len(branch_info) != 2:
173
raise errors.UnexpectedSmartServerResponse(response)
174
branch_ref, branch_name = branch_info
175
format = bzrdir.network_format_registry.get(control_name)
177
format.repository_format = repository.network_format_registry.get(
179
if branch_ref == 'ref':
180
# XXX: we need possible_transports here to avoid reopening the
181
# connection to the referenced location
182
ref_bzrdir = BzrDir.open(branch_name)
183
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
184
format.set_branch_format(branch_format)
185
elif branch_ref == 'branch':
187
format.set_branch_format(
188
branch.network_format_registry.get(branch_name))
190
raise errors.UnexpectedSmartServerResponse(response)
91
193
def create_repository(self, shared=False):
93
self._real_bzrdir.create_repository(shared=shared)
94
return self.open_repository()
194
# as per meta1 formats - just delegate to the format object which may
196
result = self._format.repository_format.initialize(self, shared)
197
if not isinstance(result, RemoteRepository):
198
return self.open_repository()
96
202
def destroy_repository(self):
97
203
"""See BzrDir.destroy_repository"""
122
241
def get_branch_reference(self):
123
242
"""See BzrDir.get_branch_reference()."""
243
response = self._get_branch_reference()
244
if response[0] == 'ref':
249
def _get_branch_reference(self):
124
250
path = self._path_for_remote_call(self._client)
126
response = self._client.call('BzrDir.open_branch', path)
127
except errors.ErrorFromSmartServer, err:
128
self._translate_error(err)
129
if response[0] == 'ok':
130
if response[1] == '':
131
# branch at this location.
134
# a branch reference, use the existing BranchReference logic.
251
medium = self._client._medium
252
if not medium._is_remote_before((1, 13)):
254
response = self._call('BzrDir.open_branchV2', path)
255
if response[0] not in ('ref', 'branch'):
256
raise errors.UnexpectedSmartServerResponse(response)
258
except errors.UnknownSmartMethod:
259
medium._remember_remote_is_before((1, 13))
260
response = self._call('BzrDir.open_branch', path)
261
if response[0] != 'ok':
137
262
raise errors.UnexpectedSmartServerResponse(response)
263
if response[1] != '':
264
return ('ref', response[1])
266
return ('branch', '')
139
268
def _get_tree_branch(self):
140
269
"""See BzrDir._get_tree_branch()."""
141
270
return None, self.open_branch()
143
def open_branch(self, _unsupported=False):
272
def open_branch(self, _unsupported=False, ignore_fallbacks=False):
145
274
raise NotImplementedError('unsupported flag support not implemented yet.')
146
reference_url = self.get_branch_reference()
147
if reference_url is None:
148
# branch at this location.
149
return RemoteBranch(self, self.find_repository())
275
if self._next_open_branch_result is not None:
276
# See create_branch for details.
277
result = self._next_open_branch_result
278
self._next_open_branch_result = None
280
response = self._get_branch_reference()
281
if response[0] == 'ref':
151
282
# a branch reference, use the existing BranchReference logic.
152
283
format = BranchReferenceFormat()
153
return format.open(self, _found=True, location=reference_url)
284
return format.open(self, _found=True, location=response[1],
285
ignore_fallbacks=ignore_fallbacks)
286
branch_format_name = response[1]
287
if not branch_format_name:
288
branch_format_name = None
289
format = RemoteBranchFormat(network_name=branch_format_name)
290
return RemoteBranch(self, self.find_repository(), format=format,
291
setup_stacking=not ignore_fallbacks)
293
def _open_repo_v1(self, path):
294
verb = 'BzrDir.find_repository'
295
response = self._call(verb, path)
296
if response[0] != 'ok':
297
raise errors.UnexpectedSmartServerResponse(response)
298
# servers that only support the v1 method don't support external
301
repo = self._real_bzrdir.open_repository()
302
response = response + ('no', repo._format.network_name())
303
return response, repo
305
def _open_repo_v2(self, path):
306
verb = 'BzrDir.find_repositoryV2'
307
response = self._call(verb, path)
308
if response[0] != 'ok':
309
raise errors.UnexpectedSmartServerResponse(response)
311
repo = self._real_bzrdir.open_repository()
312
response = response + (repo._format.network_name(),)
313
return response, repo
315
def _open_repo_v3(self, path):
316
verb = 'BzrDir.find_repositoryV3'
317
medium = self._client._medium
318
if medium._is_remote_before((1, 13)):
319
raise errors.UnknownSmartMethod(verb)
321
response = self._call(verb, path)
322
except errors.UnknownSmartMethod:
323
medium._remember_remote_is_before((1, 13))
325
if response[0] != 'ok':
326
raise errors.UnexpectedSmartServerResponse(response)
327
return response, None
155
329
def open_repository(self):
156
330
path = self._path_for_remote_call(self._client)
157
verb = 'BzrDir.find_repositoryV2'
332
for probe in [self._open_repo_v3, self._open_repo_v2,
160
response = self._client.call(verb, path)
335
response, real_repo = probe(path)
161
337
except errors.UnknownSmartMethod:
162
verb = 'BzrDir.find_repository'
163
response = self._client.call(verb, path)
164
except errors.ErrorFromSmartServer, err:
165
self._translate_error(err)
340
raise errors.UnknownSmartMethod('BzrDir.find_repository{3,2,}')
166
341
if response[0] != 'ok':
167
342
raise errors.UnexpectedSmartServerResponse(response)
168
if verb == 'BzrDir.find_repository':
169
# servers that don't support the V2 method don't support external
171
response = response + ('no', )
172
if not (len(response) == 5):
343
if len(response) != 6:
173
344
raise SmartProtocolError('incorrect response length %s' % (response,))
174
345
if response[1] == '':
175
format = RemoteRepositoryFormat()
176
format.rich_root_data = (response[2] == 'yes')
177
format.supports_tree_reference = (response[3] == 'yes')
178
# No wire format to check this yet.
179
format.supports_external_lookups = (response[4] == 'yes')
346
# repo is at this dir.
347
format = response_tuple_to_repo_format(response[2:])
180
348
# Used to support creating a real format instance when needed.
181
349
format._creating_bzrdir = self
182
return RemoteRepository(self, format)
350
remote_repo = RemoteRepository(self, format)
351
format._creating_repo = remote_repo
352
if real_repo is not None:
353
remote_repo._set_real_repository(real_repo)
184
356
raise errors.NoRepositoryPresent(self)
236
410
the attributes rich_root_data and supports_tree_reference are set
237
411
on a per instance basis, and are not set (and should not be) at
414
:ivar _custom_format: If set, a specific concrete repository format that
415
will be used when initializing a repository with this
416
RemoteRepositoryFormat.
417
:ivar _creating_repo: If set, the repository object that this
418
RemoteRepositoryFormat was created for: it can be called into
419
to obtain data like the network name.
241
422
_matchingbzrdir = RemoteBzrDirFormat()
243
def initialize(self, a_bzrdir, shared=False):
244
if not isinstance(a_bzrdir, RemoteBzrDir):
425
repository.RepositoryFormat.__init__(self)
426
self._custom_format = None
427
self._network_name = None
428
self._creating_bzrdir = None
429
self._supports_chks = None
430
self._supports_external_lookups = None
431
self._supports_tree_reference = None
432
self._rich_root_data = None
435
return "%s(_network_name=%r)" % (self.__class__.__name__,
439
def fast_deltas(self):
441
return self._custom_format.fast_deltas
444
def rich_root_data(self):
445
if self._rich_root_data is None:
447
self._rich_root_data = self._custom_format.rich_root_data
448
return self._rich_root_data
451
def supports_chks(self):
452
if self._supports_chks is None:
454
self._supports_chks = self._custom_format.supports_chks
455
return self._supports_chks
458
def supports_external_lookups(self):
459
if self._supports_external_lookups is None:
461
self._supports_external_lookups = \
462
self._custom_format.supports_external_lookups
463
return self._supports_external_lookups
466
def supports_tree_reference(self):
467
if self._supports_tree_reference is None:
469
self._supports_tree_reference = \
470
self._custom_format.supports_tree_reference
471
return self._supports_tree_reference
473
def _vfs_initialize(self, a_bzrdir, shared):
474
"""Helper for common code in initialize."""
475
if self._custom_format:
476
# Custom format requested
477
result = self._custom_format.initialize(a_bzrdir, shared=shared)
478
elif self._creating_bzrdir is not None:
479
# Use the format that the repository we were created to back
245
481
prior_repo = self._creating_bzrdir.open_repository()
246
482
prior_repo._ensure_real()
247
return prior_repo._real_repository._format.initialize(
483
result = prior_repo._real_repository._format.initialize(
248
484
a_bzrdir, shared=shared)
249
return a_bzrdir.create_repository(shared=shared)
486
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
487
# support remote initialization.
488
# We delegate to a real object at this point (as RemoteBzrDir
489
# delegate to the repository format which would lead to infinite
490
# recursion if we just called a_bzrdir.create_repository.
491
a_bzrdir._ensure_real()
492
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
493
if not isinstance(result, RemoteRepository):
494
return self.open(a_bzrdir)
498
def initialize(self, a_bzrdir, shared=False):
499
# Being asked to create on a non RemoteBzrDir:
500
if not isinstance(a_bzrdir, RemoteBzrDir):
501
return self._vfs_initialize(a_bzrdir, shared)
502
medium = a_bzrdir._client._medium
503
if medium._is_remote_before((1, 13)):
504
return self._vfs_initialize(a_bzrdir, shared)
505
# Creating on a remote bzr dir.
506
# 1) get the network name to use.
507
if self._custom_format:
508
network_name = self._custom_format.network_name()
509
elif self._network_name:
510
network_name = self._network_name
512
# Select the current bzrlib default and ask for that.
513
reference_bzrdir_format = bzrdir.format_registry.get('default')()
514
reference_format = reference_bzrdir_format.repository_format
515
network_name = reference_format.network_name()
516
# 2) try direct creation via RPC
517
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
518
verb = 'BzrDir.create_repository'
524
response = a_bzrdir._call(verb, path, network_name, shared_str)
525
except errors.UnknownSmartMethod:
526
# Fallback - use vfs methods
527
medium._remember_remote_is_before((1, 13))
528
return self._vfs_initialize(a_bzrdir, shared)
530
# Turn the response into a RemoteRepository object.
531
format = response_tuple_to_repo_format(response[1:])
532
# Used to support creating a real format instance when needed.
533
format._creating_bzrdir = a_bzrdir
534
remote_repo = RemoteRepository(a_bzrdir, format)
535
format._creating_repo = remote_repo
251
538
def open(self, a_bzrdir):
252
539
if not isinstance(a_bzrdir, RemoteBzrDir):
253
540
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
254
541
return a_bzrdir.open_repository()
543
def _ensure_real(self):
544
if self._custom_format is None:
545
self._custom_format = repository.network_format_registry.get(
549
def _fetch_order(self):
551
return self._custom_format._fetch_order
554
def _fetch_uses_deltas(self):
556
return self._custom_format._fetch_uses_deltas
559
def _fetch_reconcile(self):
561
return self._custom_format._fetch_reconcile
256
563
def get_format_description(self):
257
564
return 'bzr remote repository'
259
566
def __eq__(self, other):
260
return self.__class__ == other.__class__
262
def check_conversion_target(self, target_format):
263
if self.rich_root_data and not target_format.rich_root_data:
264
raise errors.BadConversionTarget(
265
'Does not support rich root data.', target_format)
266
if (self.supports_tree_reference and
267
not getattr(target_format, 'supports_tree_reference', False)):
268
raise errors.BadConversionTarget(
269
'Does not support nested trees', target_format)
272
class RemoteRepository(object):
567
return self.__class__ is other.__class__
569
def network_name(self):
570
if self._network_name:
571
return self._network_name
572
self._creating_repo._ensure_real()
573
return self._creating_repo._real_repository._format.network_name()
576
def pack_compresses(self):
578
return self._custom_format.pack_compresses
581
def _serializer(self):
583
return self._custom_format._serializer
586
class RemoteRepository(_RpcHelper):
273
587
"""Repository accessed over rpc.
275
589
For the moment most operations are performed using local transport-backed
344
672
self._ensure_real()
345
673
return self._real_repository.commit_write_group()
675
def resume_write_group(self, tokens):
677
return self._real_repository.resume_write_group(tokens)
679
def suspend_write_group(self):
681
return self._real_repository.suspend_write_group()
683
def get_missing_parent_inventories(self, check_for_missing_texts=True):
685
return self._real_repository.get_missing_parent_inventories(
686
check_for_missing_texts=check_for_missing_texts)
688
def _get_rev_id_for_revno_vfs(self, revno, known_pair):
690
return self._real_repository.get_rev_id_for_revno(
693
def get_rev_id_for_revno(self, revno, known_pair):
694
"""See Repository.get_rev_id_for_revno."""
695
path = self.bzrdir._path_for_remote_call(self._client)
697
if self._client._medium._is_remote_before((1, 17)):
698
return self._get_rev_id_for_revno_vfs(revno, known_pair)
699
response = self._call(
700
'Repository.get_rev_id_for_revno', path, revno, known_pair)
701
except errors.UnknownSmartMethod:
702
self._client._medium._remember_remote_is_before((1, 17))
703
return self._get_rev_id_for_revno_vfs(revno, known_pair)
704
if response[0] == 'ok':
705
return True, response[1]
706
elif response[0] == 'history-incomplete':
707
known_pair = response[1:3]
708
for fallback in self._fallback_repositories:
709
found, result = fallback.get_rev_id_for_revno(revno, known_pair)
714
# Not found in any fallbacks
715
return False, known_pair
717
raise errors.UnexpectedSmartServerResponse(response)
347
719
def _ensure_real(self):
348
720
"""Ensure that there is a _real_repository set.
350
722
Used before calls to self._real_repository.
724
Note that _ensure_real causes many roundtrips to the server which are
725
not desirable, and prevents the use of smart one-roundtrip RPC's to
726
perform complex operations (such as accessing parent data, streaming
727
revisions etc). Adding calls to _ensure_real should only be done when
728
bringing up new functionality, adding fallbacks for smart methods that
729
require a fallback path, and never to replace an existing smart method
730
invocation. If in doubt chat to the bzr network team.
352
732
if self._real_repository is None:
733
if 'hpssvfs' in debug.debug_flags:
735
warning('VFS Repository access triggered\n%s',
736
''.join(traceback.format_stack()))
737
self._unstacked_provider.missing_keys.clear()
353
738
self.bzrdir._ensure_real()
354
739
self._set_real_repository(
355
740
self.bzrdir._real_bzrdir.open_repository())
412
789
for line in lines:
413
790
d = tuple(line.split())
414
791
revision_graph[d[0]] = d[1:]
416
793
return revision_graph
796
"""See Repository._get_sink()."""
797
return RemoteStreamSink(self)
799
def _get_source(self, to_format):
800
"""Return a source for streaming from this repository."""
801
return RemoteStreamSource(self, to_format)
418
804
def has_revision(self, revision_id):
419
"""See Repository.has_revision()."""
420
if revision_id == NULL_REVISION:
421
# The null revision is always present.
423
path = self.bzrdir._path_for_remote_call(self._client)
424
response = self._client.call(
425
'Repository.has_revision', path, revision_id)
426
if response[0] not in ('yes', 'no'):
427
raise errors.UnexpectedSmartServerResponse(response)
428
return response[0] == 'yes'
805
"""True if this repository has a copy of the revision."""
806
# Copy of bzrlib.repository.Repository.has_revision
807
return revision_id in self.has_revisions((revision_id,))
430
810
def has_revisions(self, revision_ids):
431
"""See Repository.has_revisions()."""
433
for revision_id in revision_ids:
434
if self.has_revision(revision_id):
435
result.add(revision_id)
811
"""Probe to find out the presence of multiple revisions.
813
:param revision_ids: An iterable of revision_ids.
814
:return: A set of the revision_ids that were present.
816
# Copy of bzrlib.repository.Repository.has_revisions
817
parent_map = self.get_parent_map(revision_ids)
818
result = set(parent_map)
819
if _mod_revision.NULL_REVISION in revision_ids:
820
result.add(_mod_revision.NULL_REVISION)
823
def _has_same_fallbacks(self, other_repo):
824
"""Returns true if the repositories have the same fallbacks."""
825
# XXX: copied from Repository; it should be unified into a base class
826
# <https://bugs.edge.launchpad.net/bzr/+bug/401622>
827
my_fb = self._fallback_repositories
828
other_fb = other_repo._fallback_repositories
829
if len(my_fb) != len(other_fb):
831
for f, g in zip(my_fb, other_fb):
832
if not f.has_same_location(g):
438
836
def has_same_location(self, other):
439
return (self.__class__ == other.__class__ and
837
# TODO: Move to RepositoryBase and unify with the regular Repository
838
# one; unfortunately the tests rely on slightly different behaviour at
839
# present -- mbp 20090710
840
return (self.__class__ is other.__class__ and
440
841
self.bzrdir.transport.base == other.bzrdir.transport.base)
442
843
def get_graph(self, other_repository=None):
443
844
"""Return the graph for this repository format"""
444
parents_provider = self
445
if (other_repository is not None and
446
other_repository.bzrdir.transport.base !=
447
self.bzrdir.transport.base):
448
parents_provider = graph._StackedParentsProvider(
449
[parents_provider, other_repository._make_parents_provider()])
845
parents_provider = self._make_parents_provider(other_repository)
450
846
return graph.Graph(parents_provider)
452
848
def gather_stats(self, revid=None, committers=None):
811
1286
self._ensure_real()
812
1287
return self._real_repository._get_versioned_file_checker(
813
1288
revisions, revision_versions_cache)
815
1290
def iter_files_bytes(self, desired_files):
816
1291
"""See Repository.iter_file_bytes.
818
1293
self._ensure_real()
819
1294
return self._real_repository.iter_files_bytes(desired_files)
822
def _fetch_order(self):
823
"""Decorate the real repository for now.
825
In the long term getting this back from the remote repository as part
826
of open would be more efficient.
829
return self._real_repository._fetch_order
832
def _fetch_uses_deltas(self):
833
"""Decorate the real repository for now.
835
In the long term getting this back from the remote repository as part
836
of open would be more efficient.
839
return self._real_repository._fetch_uses_deltas
842
def _fetch_reconcile(self):
843
"""Decorate the real repository for now.
845
In the long term getting this back from the remote repository as part
846
of open would be more efficient.
849
return self._real_repository._fetch_reconcile
851
def get_parent_map(self, keys):
1296
def get_parent_map(self, revision_ids):
852
1297
"""See bzrlib.Graph.get_parent_map()."""
853
# Hack to build up the caching logic.
854
ancestry = self._parents_map
856
# Repository is not locked, so there's no cache.
857
missing_revisions = set(keys)
860
missing_revisions = set(key for key in keys if key not in ancestry)
861
if missing_revisions:
862
parent_map = self._get_parent_map(missing_revisions)
863
if 'hpss' in debug.debug_flags:
864
mutter('retransmitted revisions: %d of %d',
865
len(set(ancestry).intersection(parent_map)),
867
ancestry.update(parent_map)
868
present_keys = [k for k in keys if k in ancestry]
869
if 'hpss' in debug.debug_flags:
870
if self._requested_parents is not None and len(ancestry) != 0:
871
self._requested_parents.update(present_keys)
872
mutter('Current RemoteRepository graph hit rate: %d%%',
873
100.0 * len(self._requested_parents) / len(ancestry))
874
return dict((k, ancestry[k]) for k in present_keys)
1298
return self._make_parents_provider().get_parent_map(revision_ids)
876
def _get_parent_map(self, keys):
1300
def _get_parent_map_rpc(self, keys):
877
1301
"""Helper for get_parent_map that performs the RPC."""
878
1302
medium = self._client._medium
879
1303
if medium._is_remote_before((1, 2)):
880
1304
# We already found out that the server can't understand
881
1305
# Repository.get_parent_map requests, so just fetch the whole
883
# XXX: Note that this will issue a deprecation warning. This is ok
884
# :- its because we're working with a deprecated server anyway, and
885
# the user will almost certainly have seen a warning about the
886
# server version already.
887
rg = self.get_revision_graph()
888
# There is an api discrepency between get_parent_map and
1308
# Note that this reads the whole graph, when only some keys are
1309
# wanted. On this old server there's no way (?) to get them all
1310
# in one go, and the user probably will have seen a warning about
1311
# the server being old anyhow.
1312
rg = self._get_revision_graph(None)
1313
# There is an API discrepancy between get_parent_map and
889
1314
# get_revision_graph. Specifically, a "key:()" pair in
890
1315
# get_revision_graph just means a node has no parents. For
891
1316
# "get_parent_map" it means the node is a ghost. So fix up the
1182
1645
:param recipe: A search recipe (start, stop, count).
1183
1646
:return: Serialised bytes.
1185
start_keys = ' '.join(recipe[0])
1186
stop_keys = ' '.join(recipe[1])
1187
count = str(recipe[2])
1648
start_keys = ' '.join(recipe[1])
1649
stop_keys = ' '.join(recipe[2])
1650
count = str(recipe[3])
1188
1651
return '\n'.join((start_keys, stop_keys, count))
1653
def _serialise_search_result(self, search_result):
1654
if isinstance(search_result, graph.PendingAncestryResult):
1655
parts = ['ancestry-of']
1656
parts.extend(search_result.heads)
1658
recipe = search_result.get_recipe()
1659
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1660
return '\n'.join(parts)
1663
path = self.bzrdir._path_for_remote_call(self._client)
1665
response = self._call('PackRepository.autopack', path)
1666
except errors.UnknownSmartMethod:
1668
self._real_repository._pack_collection.autopack()
1671
if response[0] != 'ok':
1672
raise errors.UnexpectedSmartServerResponse(response)
1675
class RemoteStreamSink(repository.StreamSink):
1677
def _insert_real(self, stream, src_format, resume_tokens):
1678
self.target_repo._ensure_real()
1679
sink = self.target_repo._real_repository._get_sink()
1680
result = sink.insert_stream(stream, src_format, resume_tokens)
1682
self.target_repo.autopack()
1685
def insert_stream(self, stream, src_format, resume_tokens):
1686
target = self.target_repo
1687
target._unstacked_provider.missing_keys.clear()
1688
candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
1689
if target._lock_token:
1690
candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
1691
lock_args = (target._lock_token or '',)
1693
candidate_calls.append(('Repository.insert_stream', (1, 13)))
1695
client = target._client
1696
medium = client._medium
1697
path = target.bzrdir._path_for_remote_call(client)
1698
# Probe for the verb to use with an empty stream before sending the
1699
# real stream to it. We do this both to avoid the risk of sending a
1700
# large request that is then rejected, and because we don't want to
1701
# implement a way to buffer, rewind, or restart the stream.
1703
for verb, required_version in candidate_calls:
1704
if medium._is_remote_before(required_version):
1707
# We've already done the probing (and set _is_remote_before) on
1708
# a previous insert.
1711
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
1713
response = client.call_with_body_stream(
1714
(verb, path, '') + lock_args, byte_stream)
1715
except errors.UnknownSmartMethod:
1716
medium._remember_remote_is_before(required_version)
1722
return self._insert_real(stream, src_format, resume_tokens)
1723
self._last_inv_record = None
1724
self._last_substream = None
1725
if required_version < (1, 19):
1726
# Remote side doesn't support inventory deltas. Wrap the stream to
1727
# make sure we don't send any. If the stream contains inventory
1728
# deltas we'll interrupt the smart insert_stream request and
1730
stream = self._stop_stream_if_inventory_delta(stream)
1731
byte_stream = smart_repo._stream_to_byte_stream(
1733
resume_tokens = ' '.join(resume_tokens)
1734
response = client.call_with_body_stream(
1735
(verb, path, resume_tokens) + lock_args, byte_stream)
1736
if response[0][0] not in ('ok', 'missing-basis'):
1737
raise errors.UnexpectedSmartServerResponse(response)
1738
if self._last_substream is not None:
1739
# The stream included an inventory-delta record, but the remote
1740
# side isn't new enough to support them. So we need to send the
1741
# rest of the stream via VFS.
1742
return self._resume_stream_with_vfs(response, src_format)
1743
if response[0][0] == 'missing-basis':
1744
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1745
resume_tokens = tokens
1746
return resume_tokens, set(missing_keys)
1748
self.target_repo.refresh_data()
1751
def _resume_stream_with_vfs(self, response, src_format):
1752
"""Resume sending a stream via VFS, first resending the record and
1753
substream that couldn't be sent via an insert_stream verb.
1755
if response[0][0] == 'missing-basis':
1756
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
1757
# Ignore missing_keys, we haven't finished inserting yet
1760
def resume_substream():
1761
# Yield the substream that was interrupted.
1762
for record in self._last_substream:
1764
self._last_substream = None
1765
def resume_stream():
1766
# Finish sending the interrupted substream
1767
yield ('inventory-deltas', resume_substream())
1768
# Then simply continue sending the rest of the stream.
1769
for substream_kind, substream in self._last_stream:
1770
yield substream_kind, substream
1771
return self._insert_real(resume_stream(), src_format, tokens)
1773
def _stop_stream_if_inventory_delta(self, stream):
1774
"""Normally this just lets the original stream pass-through unchanged.
1776
However if any 'inventory-deltas' substream occurs it will stop
1777
streaming, and store the interrupted substream and stream in
1778
self._last_substream and self._last_stream so that the stream can be
1779
resumed by _resume_stream_with_vfs.
1782
stream_iter = iter(stream)
1783
for substream_kind, substream in stream_iter:
1784
if substream_kind == 'inventory-deltas':
1785
self._last_substream = substream
1786
self._last_stream = stream_iter
1789
yield substream_kind, substream
1792
class RemoteStreamSource(repository.StreamSource):
1793
"""Stream data from a remote server."""
1795
def get_stream(self, search):
1796
if (self.from_repository._fallback_repositories and
1797
self.to_format._fetch_order == 'topological'):
1798
return self._real_stream(self.from_repository, search)
1801
repos = [self.from_repository]
1807
repos.extend(repo._fallback_repositories)
1808
sources.append(repo)
1809
return self.missing_parents_chain(search, sources)
1811
def get_stream_for_missing_keys(self, missing_keys):
1812
self.from_repository._ensure_real()
1813
real_repo = self.from_repository._real_repository
1814
real_source = real_repo._get_source(self.to_format)
1815
return real_source.get_stream_for_missing_keys(missing_keys)
1817
def _real_stream(self, repo, search):
1818
"""Get a stream for search from repo.
1820
This never called RemoteStreamSource.get_stream, and is a heler
1821
for RemoteStreamSource._get_stream to allow getting a stream
1822
reliably whether fallback back because of old servers or trying
1823
to stream from a non-RemoteRepository (which the stacked support
1826
source = repo._get_source(self.to_format)
1827
if isinstance(source, RemoteStreamSource):
1829
source = repo._real_repository._get_source(self.to_format)
1830
return source.get_stream(search)
1832
def _get_stream(self, repo, search):
1833
"""Core worker to get a stream from repo for search.
1835
This is used by both get_stream and the stacking support logic. It
1836
deliberately gets a stream for repo which does not need to be
1837
self.from_repository. In the event that repo is not Remote, or
1838
cannot do a smart stream, a fallback is made to the generic
1839
repository._get_stream() interface, via self._real_stream.
1841
In the event of stacking, streams from _get_stream will not
1842
contain all the data for search - this is normal (see get_stream).
1844
:param repo: A repository.
1845
:param search: A search.
1847
# Fallbacks may be non-smart
1848
if not isinstance(repo, RemoteRepository):
1849
return self._real_stream(repo, search)
1850
client = repo._client
1851
medium = client._medium
1852
path = repo.bzrdir._path_for_remote_call(client)
1853
search_bytes = repo._serialise_search_result(search)
1854
args = (path, self.to_format.network_name())
1856
('Repository.get_stream_1.19', (1, 19)),
1857
('Repository.get_stream', (1, 13))]
1859
for verb, version in candidate_verbs:
1860
if medium._is_remote_before(version):
1863
response = repo._call_with_body_bytes_expecting_body(
1864
verb, args, search_bytes)
1865
except errors.UnknownSmartMethod:
1866
medium._remember_remote_is_before(version)
1868
response_tuple, response_handler = response
1872
return self._real_stream(repo, search)
1873
if response_tuple[0] != 'ok':
1874
raise errors.UnexpectedSmartServerResponse(response_tuple)
1875
byte_stream = response_handler.read_streamed_body()
1876
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
1877
if src_format.network_name() != repo._format.network_name():
1878
raise AssertionError(
1879
"Mismatched RemoteRepository and stream src %r, %r" % (
1880
src_format.network_name(), repo._format.network_name()))
1883
def missing_parents_chain(self, search, sources):
1884
"""Chain multiple streams together to handle stacking.
1886
:param search: The overall search to satisfy with streams.
1887
:param sources: A list of Repository objects to query.
1889
self.serialiser = self.to_format._serializer
1890
self.seen_revs = set()
1891
self.referenced_revs = set()
1892
# If there are heads in the search, or the key count is > 0, we are not
1894
while not search.is_empty() and len(sources) > 1:
1895
source = sources.pop(0)
1896
stream = self._get_stream(source, search)
1897
for kind, substream in stream:
1898
if kind != 'revisions':
1899
yield kind, substream
1901
yield kind, self.missing_parents_rev_handler(substream)
1902
search = search.refine(self.seen_revs, self.referenced_revs)
1903
self.seen_revs = set()
1904
self.referenced_revs = set()
1905
if not search.is_empty():
1906
for kind, stream in self._get_stream(sources[0], search):
1909
def missing_parents_rev_handler(self, substream):
1910
for content in substream:
1911
revision_bytes = content.get_bytes_as('fulltext')
1912
revision = self.serialiser.read_revision_from_string(revision_bytes)
1913
self.seen_revs.add(content.key[-1])
1914
self.referenced_revs.update(revision.parent_ids)
1191
1918
class RemoteBranchLockableFiles(LockableFiles):
1192
1919
"""A 'LockableFiles' implementation that talks to a smart server.
1194
1921
This is not a public interface class.
1211
1938
class RemoteBranchFormat(branch.BranchFormat):
1940
def __init__(self, network_name=None):
1941
super(RemoteBranchFormat, self).__init__()
1942
self._matchingbzrdir = RemoteBzrDirFormat()
1943
self._matchingbzrdir.set_branch_format(self)
1944
self._custom_format = None
1945
self._network_name = network_name
1213
1947
def __eq__(self, other):
1214
return (isinstance(other, RemoteBranchFormat) and
1948
return (isinstance(other, RemoteBranchFormat) and
1215
1949
self.__dict__ == other.__dict__)
1951
def _ensure_real(self):
1952
if self._custom_format is None:
1953
self._custom_format = branch.network_format_registry.get(
1217
1956
def get_format_description(self):
1218
1957
return 'Remote BZR Branch'
1220
def get_format_string(self):
1221
return 'Remote BZR Branch'
1223
def open(self, a_bzrdir):
1224
return a_bzrdir.open_branch()
1959
def network_name(self):
1960
return self._network_name
1962
def open(self, a_bzrdir, ignore_fallbacks=False):
1963
return a_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
1965
def _vfs_initialize(self, a_bzrdir):
1966
# Initialisation when using a local bzrdir object, or a non-vfs init
1967
# method is not available on the server.
1968
# self._custom_format is always set - the start of initialize ensures
1970
if isinstance(a_bzrdir, RemoteBzrDir):
1971
a_bzrdir._ensure_real()
1972
result = self._custom_format.initialize(a_bzrdir._real_bzrdir)
1974
# We assume the bzrdir is parameterised; it may not be.
1975
result = self._custom_format.initialize(a_bzrdir)
1976
if (isinstance(a_bzrdir, RemoteBzrDir) and
1977
not isinstance(result, RemoteBranch)):
1978
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result)
1226
1981
def initialize(self, a_bzrdir):
1227
return a_bzrdir.create_branch()
1982
# 1) get the network name to use.
1983
if self._custom_format:
1984
network_name = self._custom_format.network_name()
1986
# Select the current bzrlib default and ask for that.
1987
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1988
reference_format = reference_bzrdir_format.get_branch_format()
1989
self._custom_format = reference_format
1990
network_name = reference_format.network_name()
1991
# Being asked to create on a non RemoteBzrDir:
1992
if not isinstance(a_bzrdir, RemoteBzrDir):
1993
return self._vfs_initialize(a_bzrdir)
1994
medium = a_bzrdir._client._medium
1995
if medium._is_remote_before((1, 13)):
1996
return self._vfs_initialize(a_bzrdir)
1997
# Creating on a remote bzr dir.
1998
# 2) try direct creation via RPC
1999
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2000
verb = 'BzrDir.create_branch'
2002
response = a_bzrdir._call(verb, path, network_name)
2003
except errors.UnknownSmartMethod:
2004
# Fallback - use vfs methods
2005
medium._remember_remote_is_before((1, 13))
2006
return self._vfs_initialize(a_bzrdir)
2007
if response[0] != 'ok':
2008
raise errors.UnexpectedSmartServerResponse(response)
2009
# Turn the response into a RemoteRepository object.
2010
format = RemoteBranchFormat(network_name=response[1])
2011
repo_format = response_tuple_to_repo_format(response[3:])
2012
if response[2] == '':
2013
repo_bzrdir = a_bzrdir
2015
repo_bzrdir = RemoteBzrDir(
2016
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2018
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2019
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2020
format=format, setup_stacking=False)
2021
# XXX: We know this is a new branch, so it must have revno 0, revid
2022
# NULL_REVISION. Creating the branch locked would make this be unable
2023
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2024
remote_branch._last_revision_info_cache = 0, NULL_REVISION
2025
return remote_branch
2027
def make_tags(self, branch):
2029
return self._custom_format.make_tags(branch)
1229
2031
def supports_tags(self):
1230
2032
# Remote branches might support tags, but we won't know until we
1231
2033
# access the real remote branch.
1235
class RemoteBranch(branch.Branch):
2035
return self._custom_format.supports_tags()
2037
def supports_stacking(self):
2039
return self._custom_format.supports_stacking()
2041
def supports_set_append_revisions_only(self):
2043
return self._custom_format.supports_set_append_revisions_only()
2046
class RemoteBranch(branch.Branch, _RpcHelper):
1236
2047
"""Branch stored on a server accessed by HPSS RPC.
1238
2049
At the moment most operations are mapped down to simple file operations.
1241
2052
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
2053
_client=None, format=None, setup_stacking=True):
1243
2054
"""Create a RemoteBranch instance.
1245
2056
:param real_branch: An optional local implementation of the branch
1246
2057
format, usually accessing the data via the VFS.
1247
2058
:param _client: Private parameter for testing.
2059
:param format: A RemoteBranchFormat object, None to create one
2060
automatically. If supplied it should have a network_name already
2062
:param setup_stacking: If True make an RPC call to determine the
2063
stacked (or not) status of the branch. If False assume the branch
1249
2066
# We intentionally don't call the parent class's __init__, because it
1250
2067
# will try to assign to self.tags, which is a property in this subclass.
1251
2068
# And the parent's __init__ doesn't do much anyway.
1252
self._revision_id_to_revno_cache = None
1253
self._revision_history_cache = None
1254
self._last_revision_info_cache = None
1255
2069
self.bzrdir = remote_bzrdir
1256
2070
if _client is not None:
1257
2071
self._client = _client
1366
2221
:raises UnstackableRepositoryFormat: If the repository does not support
1370
return self._real_branch.get_stacked_on_url()
2225
# there may not be a repository yet, so we can't use
2226
# self._translate_error, so we can't use self._call either.
2227
response = self._client.call('Branch.get_stacked_on_url',
2228
self._remote_path())
2229
except errors.ErrorFromSmartServer, err:
2230
# there may not be a repository yet, so we can't call through
2231
# its _translate_error
2232
_translate_error(err, branch=self)
2233
except errors.UnknownSmartMethod, err:
2235
return self._real_branch.get_stacked_on_url()
2236
if response[0] != 'ok':
2237
raise errors.UnexpectedSmartServerResponse(response)
2240
def set_stacked_on_url(self, url):
2241
branch.Branch.set_stacked_on_url(self, url)
2243
self._is_stacked = False
2245
self._is_stacked = True
2247
def _vfs_get_tags_bytes(self):
2249
return self._real_branch._get_tags_bytes()
2251
def _get_tags_bytes(self):
2252
medium = self._client._medium
2253
if medium._is_remote_before((1, 13)):
2254
return self._vfs_get_tags_bytes()
2256
response = self._call('Branch.get_tags_bytes', self._remote_path())
2257
except errors.UnknownSmartMethod:
2258
medium._remember_remote_is_before((1, 13))
2259
return self._vfs_get_tags_bytes()
2262
def _vfs_set_tags_bytes(self, bytes):
2264
return self._real_branch._set_tags_bytes(bytes)
2266
def _set_tags_bytes(self, bytes):
2267
medium = self._client._medium
2268
if medium._is_remote_before((1, 18)):
2269
self._vfs_set_tags_bytes(bytes)
2272
self._remote_path(), self._lock_token, self._repo_lock_token)
2273
response = self._call_with_body_bytes(
2274
'Branch.set_tags_bytes', args, bytes)
2275
except errors.UnknownSmartMethod:
2276
medium._remember_remote_is_before((1, 18))
2277
self._vfs_set_tags_bytes(bytes)
1372
2279
def lock_read(self):
1373
2280
self.repository.lock_read()
2431
def _remote_path(self):
2432
return self.bzrdir._path_for_remote_call(self._client)
1510
2434
def _set_last_revision_descendant(self, revision_id, other_branch,
1511
2435
allow_diverged=False, allow_overwrite_descendant=False):
1512
path = self.bzrdir._path_for_remote_call(self._client)
1514
response = self._client.call('Branch.set_last_revision_ex',
1515
path, self._lock_token, self._repo_lock_token, revision_id,
1516
int(allow_diverged), int(allow_overwrite_descendant))
1517
except errors.ErrorFromSmartServer, err:
1518
self._translate_error(err, other_branch=other_branch)
2436
# This performs additional work to meet the hook contract; while its
2437
# undesirable, we have to synthesise the revno to call the hook, and
2438
# not calling the hook is worse as it means changes can't be prevented.
2439
# Having calculated this though, we can't just call into
2440
# set_last_revision_info as a simple call, because there is a set_rh
2441
# hook that some folk may still be using.
2442
old_revno, old_revid = self.last_revision_info()
2443
history = self._lefthand_history(revision_id)
2444
self._run_pre_change_branch_tip_hooks(len(history), revision_id)
2445
err_context = {'other_branch': other_branch}
2446
response = self._call('Branch.set_last_revision_ex',
2447
self._remote_path(), self._lock_token, self._repo_lock_token,
2448
revision_id, int(allow_diverged), int(allow_overwrite_descendant),
1519
2450
self._clear_cached_state()
1520
2451
if len(response) != 3 and response[0] != 'ok':
1521
2452
raise errors.UnexpectedSmartServerResponse(response)
1522
2453
new_revno, new_revision_id = response[1:]
1523
2454
self._last_revision_info_cache = new_revno, new_revision_id
2455
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1524
2456
if self._real_branch is not None:
1525
2457
cache = new_revno, new_revision_id
1526
2458
self._real_branch._last_revision_info_cache = cache
1528
2460
def _set_last_revision(self, revision_id):
1529
path = self.bzrdir._path_for_remote_call(self._client)
2461
old_revno, old_revid = self.last_revision_info()
2462
# This performs additional work to meet the hook contract; while its
2463
# undesirable, we have to synthesise the revno to call the hook, and
2464
# not calling the hook is worse as it means changes can't be prevented.
2465
# Having calculated this though, we can't just call into
2466
# set_last_revision_info as a simple call, because there is a set_rh
2467
# hook that some folk may still be using.
2468
history = self._lefthand_history(revision_id)
2469
self._run_pre_change_branch_tip_hooks(len(history), revision_id)
1530
2470
self._clear_cached_state()
1532
response = self._client.call('Branch.set_last_revision',
1533
path, self._lock_token, self._repo_lock_token, revision_id)
1534
except errors.ErrorFromSmartServer, err:
1535
self._translate_error(err)
2471
response = self._call('Branch.set_last_revision',
2472
self._remote_path(), self._lock_token, self._repo_lock_token,
1536
2474
if response != ('ok',):
1537
2475
raise errors.UnexpectedSmartServerResponse(response)
2476
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1539
2478
@needs_write_lock
1540
2479
def set_revision_history(self, rev_history):
1547
2486
rev_id = rev_history[-1]
1548
2487
self._set_last_revision(rev_id)
2488
for hook in branch.Branch.hooks['set_rh']:
2489
hook(self, rev_history)
1549
2490
self._cache_revision_history(rev_history)
1551
def get_parent(self):
1553
return self._real_branch.get_parent()
1555
def set_parent(self, url):
1557
return self._real_branch.set_parent(url)
1559
def set_stacked_on_url(self, stacked_location):
1560
"""Set the URL this branch is stacked against.
1562
:raises UnstackableBranchFormat: If the branch does not support
1564
:raises UnstackableRepositoryFormat: If the repository does not support
1568
return self._real_branch.set_stacked_on_url(stacked_location)
1570
def sprout(self, to_bzrdir, revision_id=None):
1571
# Like Branch.sprout, except that it sprouts a branch in the default
1572
# format, because RemoteBranches can't be created at arbitrary URLs.
1573
# XXX: if to_bzrdir is a RemoteBranch, this should perhaps do
1574
# to_bzrdir.create_branch...
1576
result = self._real_branch._format.initialize(to_bzrdir)
1577
self.copy_content_into(result, revision_id=revision_id)
1578
result.set_parent(self.bzrdir.root_transport.base)
2492
def _get_parent_location(self):
2493
medium = self._client._medium
2494
if medium._is_remote_before((1, 13)):
2495
return self._vfs_get_parent_location()
2497
response = self._call('Branch.get_parent', self._remote_path())
2498
except errors.UnknownSmartMethod:
2499
medium._remember_remote_is_before((1, 13))
2500
return self._vfs_get_parent_location()
2501
if len(response) != 1:
2502
raise errors.UnexpectedSmartServerResponse(response)
2503
parent_location = response[0]
2504
if parent_location == '':
2506
return parent_location
2508
def _vfs_get_parent_location(self):
2510
return self._real_branch._get_parent_location()
2512
def _set_parent_location(self, url):
2513
medium = self._client._medium
2514
if medium._is_remote_before((1, 15)):
2515
return self._vfs_set_parent_location(url)
2517
call_url = url or ''
2518
if type(call_url) is not str:
2519
raise AssertionError('url must be a str or None (%s)' % url)
2520
response = self._call('Branch.set_parent_location',
2521
self._remote_path(), self._lock_token, self._repo_lock_token,
2523
except errors.UnknownSmartMethod:
2524
medium._remember_remote_is_before((1, 15))
2525
return self._vfs_set_parent_location(url)
2527
raise errors.UnexpectedSmartServerResponse(response)
2529
def _vfs_set_parent_location(self, url):
2531
return self._real_branch._set_parent_location(url)
1581
2533
@needs_write_lock
1582
2534
def pull(self, source, overwrite=False, stop_revision=None,
1639
2594
except errors.UnknownSmartMethod:
1640
2595
medium._remember_remote_is_before((1, 6))
1641
2596
self._clear_cached_state_of_remote_branch_only()
1643
self._real_branch.generate_revision_history(
1644
revision_id, last_rev=last_rev, other_branch=other_branch)
1649
return self._real_branch.tags
2597
self.set_revision_history(self._lefthand_history(revision_id,
2598
last_rev=last_rev,other_branch=other_branch))
1651
2600
def set_push_location(self, location):
1652
2601
self._ensure_real()
1653
2602
return self._real_branch.set_push_location(location)
1656
def update_revisions(self, other, stop_revision=None, overwrite=False,
1658
"""See Branch.update_revisions."""
2605
class RemoteConfig(object):
2606
"""A Config that reads and writes from smart verbs.
2608
It is a low-level object that considers config data to be name/value pairs
2609
that may be associated with a section. Assigning meaning to the these
2610
values is done at higher levels like bzrlib.config.TreeConfig.
2613
def get_option(self, name, section=None, default=None):
2614
"""Return the value associated with a named option.
2616
:param name: The name of the value
2617
:param section: The section the option is in (if any)
2618
:param default: The value to return if the value is not set
2619
:return: The value or default value
1661
if stop_revision is None:
1662
stop_revision = other.last_revision()
1663
if revision.is_null(stop_revision):
1664
# if there are no commits, we're done.
1666
self.fetch(other, stop_revision)
1669
# Just unconditionally set the new revision. We don't care if
1670
# the branches have diverged.
1671
self._set_last_revision(stop_revision)
2622
configobj = self._get_configobj()
2624
section_obj = configobj
1673
medium = self._client._medium
1674
if not medium._is_remote_before((1, 6)):
1676
self._set_last_revision_descendant(stop_revision, other)
1678
except errors.UnknownSmartMethod:
1679
medium._remember_remote_is_before((1, 6))
1680
# Fallback for pre-1.6 servers: check for divergence
1681
# client-side, then do _set_last_revision.
1682
last_rev = revision.ensure_null(self.last_revision())
1684
graph = self.repository.get_graph()
1685
if self._check_if_descendant_or_diverged(
1686
stop_revision, last_rev, graph, other):
1687
# stop_revision is a descendant of last_rev, but we aren't
1688
# overwriting, so we're done.
1690
self._set_last_revision(stop_revision)
2627
section_obj = configobj[section]
2630
return section_obj.get(name, default)
2631
except errors.UnknownSmartMethod:
2632
return self._vfs_get_option(name, section, default)
2634
def _response_to_configobj(self, response):
2635
if len(response[0]) and response[0][0] != 'ok':
2636
raise errors.UnexpectedSmartServerResponse(response)
2637
lines = response[1].read_body_bytes().splitlines()
2638
return config.ConfigObj(lines, encoding='utf-8')
2641
class RemoteBranchConfig(RemoteConfig):
2642
"""A RemoteConfig for Branches."""
2644
def __init__(self, branch):
2645
self._branch = branch
2647
def _get_configobj(self):
2648
path = self._branch._remote_path()
2649
response = self._branch._client.call_expecting_body(
2650
'Branch.get_config_file', path)
2651
return self._response_to_configobj(response)
2653
def set_option(self, value, name, section=None):
2654
"""Set the value associated with a named option.
2656
:param value: The value to set
2657
:param name: The name of the value to set
2658
:param section: The section the option is in (if any)
2660
medium = self._branch._client._medium
2661
if medium._is_remote_before((1, 14)):
2662
return self._vfs_set_option(value, name, section)
2664
path = self._branch._remote_path()
2665
response = self._branch._client.call('Branch.set_config_option',
2666
path, self._branch._lock_token, self._branch._repo_lock_token,
2667
value.encode('utf8'), name, section or '')
2668
except errors.UnknownSmartMethod:
2669
medium._remember_remote_is_before((1, 14))
2670
return self._vfs_set_option(value, name, section)
2672
raise errors.UnexpectedSmartServerResponse(response)
2674
def _real_object(self):
2675
self._branch._ensure_real()
2676
return self._branch._real_branch
2678
def _vfs_set_option(self, value, name, section=None):
2679
return self._real_object()._get_config().set_option(
2680
value, name, section)
2683
class RemoteBzrDirConfig(RemoteConfig):
2684
"""A RemoteConfig for BzrDirs."""
2686
def __init__(self, bzrdir):
2687
self._bzrdir = bzrdir
2689
def _get_configobj(self):
2690
medium = self._bzrdir._client._medium
2691
verb = 'BzrDir.get_config_file'
2692
if medium._is_remote_before((1, 15)):
2693
raise errors.UnknownSmartMethod(verb)
2694
path = self._bzrdir._path_for_remote_call(self._bzrdir._client)
2695
response = self._bzrdir._call_expecting_body(
2697
return self._response_to_configobj(response)
2699
def _vfs_get_option(self, name, section, default):
2700
return self._real_object()._get_config().get_option(
2701
name, section, default)
2703
def set_option(self, value, name, section=None):
2704
"""Set the value associated with a named option.
2706
:param value: The value to set
2707
:param name: The name of the value to set
2708
:param section: The section the option is in (if any)
2710
return self._real_object()._get_config().set_option(
2711
value, name, section)
2713
def _real_object(self):
2714
self._bzrdir._ensure_real()
2715
return self._bzrdir._real_bzrdir
1695
2719
def _extract_tar(tar, to_dir):
1740
2779
raise errors.DivergedBranches(find('branch'), find('other_branch'))
1741
2780
elif err.error_verb == 'TipChangeRejected':
1742
2781
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2782
elif err.error_verb == 'UnstackableBranchFormat':
2783
raise errors.UnstackableBranchFormat(*err.error_args)
2784
elif err.error_verb == 'UnstackableRepositoryFormat':
2785
raise errors.UnstackableRepositoryFormat(*err.error_args)
2786
elif err.error_verb == 'NotStacked':
2787
raise errors.NotStacked(branch=find('branch'))
2788
elif err.error_verb == 'PermissionDenied':
2790
if len(err.error_args) >= 2:
2791
extra = err.error_args[1]
2794
raise errors.PermissionDenied(path, extra=extra)
2795
elif err.error_verb == 'ReadError':
2797
raise errors.ReadError(path)
2798
elif err.error_verb == 'NoSuchFile':
2800
raise errors.NoSuchFile(path)
2801
elif err.error_verb == 'FileExists':
2802
raise errors.FileExists(err.error_args[0])
2803
elif err.error_verb == 'DirectoryNotEmpty':
2804
raise errors.DirectoryNotEmpty(err.error_args[0])
2805
elif err.error_verb == 'ShortReadvError':
2806
args = err.error_args
2807
raise errors.ShortReadvError(
2808
args[0], int(args[1]), int(args[2]), int(args[3]))
2809
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2810
encoding = str(err.error_args[0]) # encoding must always be a string
2811
val = err.error_args[1]
2812
start = int(err.error_args[2])
2813
end = int(err.error_args[3])
2814
reason = str(err.error_args[4]) # reason must always be a string
2815
if val.startswith('u:'):
2816
val = val[2:].decode('utf-8')
2817
elif val.startswith('s:'):
2818
val = val[2:].decode('base64')
2819
if err.error_verb == 'UnicodeDecodeError':
2820
raise UnicodeDecodeError(encoding, val, start, end, reason)
2821
elif err.error_verb == 'UnicodeEncodeError':
2822
raise UnicodeEncodeError(encoding, val, start, end, reason)
2823
elif err.error_verb == 'ReadOnlyError':
2824
raise errors.TransportNotPossible('readonly transport')
1743
2825
raise errors.UnknownErrorFromSmartServer(err)