93
65
method, args, body_bytes)
94
66
except errors.ErrorFromSmartServer, err:
95
67
self._translate_error(err, **err_context)
98
def response_tuple_to_repo_format(response):
99
"""Convert a response tuple describing a repository format to a format."""
100
format = RemoteRepositoryFormat()
101
format._rich_root_data = (response[0] == 'yes')
102
format._supports_tree_reference = (response[1] == 'yes')
103
format._supports_external_lookups = (response[2] == 'yes')
104
format._network_name = response[3]
108
# Note that RemoteBzrDirProber lives in bzrlib.bzrdir so bzrlib.remote
109
# does not have to be imported unless a remote format is involved.
111
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
112
"""Format representing bzrdirs accessed via a smart server"""
114
supports_workingtrees = False
116
colocated_branches = False
119
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
120
# XXX: It's a bit ugly that the network name is here, because we'd
121
# like to believe that format objects are stateless or at least
122
# immutable, However, we do at least avoid mutating the name after
123
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
124
self._network_name = None
127
return "%s(_network_name=%r)" % (self.__class__.__name__,
130
def get_format_description(self):
131
if self._network_name:
133
real_format = controldir.network_format_registry.get(
138
return 'Remote: ' + real_format.get_format_description()
139
return 'bzr remote bzrdir'
141
def get_format_string(self):
142
raise NotImplementedError(self.get_format_string)
144
def network_name(self):
145
if self._network_name:
146
return self._network_name
148
raise AssertionError("No network name set.")
150
def initialize_on_transport(self, transport):
152
# hand off the request to the smart server
153
client_medium = transport.get_smart_medium()
154
except errors.NoSmartMedium:
155
# TODO: lookup the local format from a server hint.
156
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
157
return local_dir_format.initialize_on_transport(transport)
158
client = _SmartClient(client_medium)
159
path = client.remote_path_from_transport(transport)
161
response = client.call('BzrDirFormat.initialize', path)
162
except errors.ErrorFromSmartServer, err:
163
_translate_error(err, path=path)
164
if response[0] != 'ok':
165
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
166
format = RemoteBzrDirFormat()
167
self._supply_sub_formats_to(format)
168
return RemoteBzrDir(transport, format)
170
def parse_NoneTrueFalse(self, arg):
177
raise AssertionError("invalid arg %r" % arg)
179
def _serialize_NoneTrueFalse(self, arg):
186
def _serialize_NoneString(self, arg):
189
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
190
create_prefix=False, force_new_repo=False, stacked_on=None,
191
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
194
# hand off the request to the smart server
195
client_medium = transport.get_smart_medium()
196
except errors.NoSmartMedium:
199
# Decline to open it if the server doesn't support our required
200
# version (3) so that the VFS-based transport will do it.
201
if client_medium.should_probe():
203
server_version = client_medium.protocol_version()
204
if server_version != '2':
208
except errors.SmartProtocolError:
209
# Apparently there's no usable smart server there, even though
210
# the medium supports the smart protocol.
215
client = _SmartClient(client_medium)
216
path = client.remote_path_from_transport(transport)
217
if client_medium._is_remote_before((1, 16)):
220
# TODO: lookup the local format from a server hint.
221
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
222
self._supply_sub_formats_to(local_dir_format)
223
return local_dir_format.initialize_on_transport_ex(transport,
224
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
225
force_new_repo=force_new_repo, stacked_on=stacked_on,
226
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
227
make_working_trees=make_working_trees, shared_repo=shared_repo,
229
return self._initialize_on_transport_ex_rpc(client, path, transport,
230
use_existing_dir, create_prefix, force_new_repo, stacked_on,
231
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
233
def _initialize_on_transport_ex_rpc(self, client, path, transport,
234
use_existing_dir, create_prefix, force_new_repo, stacked_on,
235
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
237
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
238
args.append(self._serialize_NoneTrueFalse(create_prefix))
239
args.append(self._serialize_NoneTrueFalse(force_new_repo))
240
args.append(self._serialize_NoneString(stacked_on))
241
# stack_on_pwd is often/usually our transport
244
stack_on_pwd = transport.relpath(stack_on_pwd)
247
except errors.PathNotChild:
249
args.append(self._serialize_NoneString(stack_on_pwd))
250
args.append(self._serialize_NoneString(repo_format_name))
251
args.append(self._serialize_NoneTrueFalse(make_working_trees))
252
args.append(self._serialize_NoneTrueFalse(shared_repo))
253
request_network_name = self._network_name or \
254
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
256
response = client.call('BzrDirFormat.initialize_ex_1.16',
257
request_network_name, path, *args)
258
except errors.UnknownSmartMethod:
259
client._medium._remember_remote_is_before((1,16))
260
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
261
self._supply_sub_formats_to(local_dir_format)
262
return local_dir_format.initialize_on_transport_ex(transport,
263
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
264
force_new_repo=force_new_repo, stacked_on=stacked_on,
265
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
266
make_working_trees=make_working_trees, shared_repo=shared_repo,
268
except errors.ErrorFromSmartServer, err:
269
_translate_error(err, path=path)
270
repo_path = response[0]
271
bzrdir_name = response[6]
272
require_stacking = response[7]
273
require_stacking = self.parse_NoneTrueFalse(require_stacking)
274
format = RemoteBzrDirFormat()
275
format._network_name = bzrdir_name
276
self._supply_sub_formats_to(format)
277
bzrdir = RemoteBzrDir(transport, format, _client=client)
279
repo_format = response_tuple_to_repo_format(response[1:])
283
repo_bzrdir_format = RemoteBzrDirFormat()
284
repo_bzrdir_format._network_name = response[5]
285
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
289
final_stack = response[8] or None
290
final_stack_pwd = response[9] or None
292
final_stack_pwd = urlutils.join(
293
transport.base, final_stack_pwd)
294
remote_repo = RemoteRepository(repo_bzr, repo_format)
295
if len(response) > 10:
296
# Updated server verb that locks remotely.
297
repo_lock_token = response[10] or None
298
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
300
remote_repo.dont_leave_lock_in_place()
302
remote_repo.lock_write()
303
policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
304
final_stack_pwd, require_stacking)
305
policy.acquire_repository()
309
bzrdir._format.set_branch_format(self.get_branch_format())
311
# The repo has already been created, but we need to make sure that
312
# we'll make a stackable branch.
313
bzrdir._format.require_stacking(_skip_repo=True)
314
return remote_repo, bzrdir, require_stacking, policy
316
def _open(self, transport):
317
return RemoteBzrDir(transport, self)
319
def __eq__(self, other):
320
if not isinstance(other, RemoteBzrDirFormat):
322
return self.get_format_description() == other.get_format_description()
324
def __return_repository_format(self):
325
# Always return a RemoteRepositoryFormat object, but if a specific bzr
326
# repository format has been asked for, tell the RemoteRepositoryFormat
327
# that it should use that for init() etc.
328
result = RemoteRepositoryFormat()
329
custom_format = getattr(self, '_repository_format', None)
331
if isinstance(custom_format, RemoteRepositoryFormat):
334
# We will use the custom format to create repositories over the
335
# wire; expose its details like rich_root_data for code to
337
result._custom_format = custom_format
340
def get_branch_format(self):
341
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
342
if not isinstance(result, RemoteBranchFormat):
343
new_result = RemoteBranchFormat()
344
new_result._custom_format = result
346
self.set_branch_format(new_result)
350
repository_format = property(__return_repository_format,
351
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
354
class RemoteControlStore(_mod_config.IniFileStore):
355
"""Control store which attempts to use HPSS calls to retrieve control store.
357
Note that this is specific to bzr-based formats.
360
def __init__(self, bzrdir):
361
super(RemoteControlStore, self).__init__()
363
self._real_store = None
365
def lock_write(self, token=None):
367
return self._real_store.lock_write(token)
371
return self._real_store.unlock()
375
# We need to be able to override the undecorated implementation
376
self.save_without_locking()
378
def save_without_locking(self):
379
super(RemoteControlStore, self).save()
381
def _ensure_real(self):
382
self.bzrdir._ensure_real()
383
if self._real_store is None:
384
self._real_store = _mod_config.ControlStore(self.bzrdir)
386
def external_url(self):
387
return urlutils.join(self.branch.user_url, 'control.conf')
389
def _load_content(self):
390
medium = self.bzrdir._client._medium
391
path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
393
response, handler = self.bzrdir._call_expecting_body(
394
'BzrDir.get_config_file', path)
395
except errors.UnknownSmartMethod:
397
return self._real_store._load_content()
398
if len(response) and response[0] != 'ok':
399
raise errors.UnexpectedSmartServerResponse(response)
400
return handler.read_body_bytes()
402
def _save_content(self, content):
403
# FIXME JRV 2011-11-22: Ideally this should use a
404
# HPSS call too, but at the moment it is not possible
405
# to write lock control directories.
407
return self._real_store._save_content(content)
410
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
69
# Note: RemoteBzrDirFormat is in bzrdir.py
71
class RemoteBzrDir(BzrDir, _RpcHelper):
411
72
"""Control directory on a remote server, accessed via bzr:// or similar."""
413
def __init__(self, transport, format, _client=None, _force_probe=False):
74
def __init__(self, transport, _client=None):
414
75
"""Construct a RemoteBzrDir.
416
77
:param _client: Private parameter for testing. Disables probing and the
417
78
use of a real bzrdir.
419
_mod_bzrdir.BzrDir.__init__(self, transport, format)
80
BzrDir.__init__(self, transport, RemoteBzrDirFormat())
420
81
# this object holds a delegated bzrdir that uses file-level operations
421
82
# to talk to the other side
422
83
self._real_bzrdir = None
423
self._has_working_tree = None
424
# 1-shot cache for the call pattern 'create_branch; open_branch' - see
425
# create_branch for details.
426
self._next_open_branch_result = None
428
85
if _client is None:
429
86
medium = transport.get_smart_medium()
430
87
self._client = client._SmartClient(medium)
432
89
self._client = _client
439
return '%s(%r)' % (self.__class__.__name__, self._client)
441
def _probe_bzrdir(self):
442
medium = self._client._medium
443
92
path = self._path_for_remote_call(self._client)
444
if medium._is_remote_before((2, 1)):
448
self._rpc_open_2_1(path)
450
except errors.UnknownSmartMethod:
451
medium._remember_remote_is_before((2, 1))
454
def _rpc_open_2_1(self, path):
455
response = self._call('BzrDir.open_2.1', path)
456
if response == ('no',):
457
raise errors.NotBranchError(path=self.root_transport.base)
458
elif response[0] == 'yes':
459
if response[1] == 'yes':
460
self._has_working_tree = True
461
elif response[1] == 'no':
462
self._has_working_tree = False
464
raise errors.UnexpectedSmartServerResponse(response)
466
raise errors.UnexpectedSmartServerResponse(response)
468
def _rpc_open(self, path):
469
93
response = self._call('BzrDir.open', path)
470
94
if response not in [('yes',), ('no',)]:
471
95
raise errors.UnexpectedSmartServerResponse(response)
472
96
if response == ('no',):
473
raise errors.NotBranchError(path=self.root_transport.base)
97
raise errors.NotBranchError(path=transport.base)
475
99
def _ensure_real(self):
476
100
"""Ensure that there is a _real_bzrdir set.
478
102
Used before calls to self._real_bzrdir.
480
104
if not self._real_bzrdir:
481
if 'hpssvfs' in debug.debug_flags:
483
warning('VFS BzrDir access triggered\n%s',
484
''.join(traceback.format_stack()))
485
self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
486
self.root_transport, probers=[_mod_bzrdir.BzrProber])
487
self._format._network_name = \
488
self._real_bzrdir._format.network_name()
105
self._real_bzrdir = BzrDir.open_from_transport(
106
self.root_transport, _server_formats=False)
490
108
def _translate_error(self, err, **context):
491
109
_translate_error(err, bzrdir=self, **context)
493
def break_lock(self):
494
# Prevent aliasing problems in the next_open_branch_result cache.
495
# See create_branch for rationale.
496
self._next_open_branch_result = None
497
return _mod_bzrdir.BzrDir.break_lock(self)
499
def _vfs_checkout_metadir(self):
501
return self._real_bzrdir.checkout_metadir()
503
def checkout_metadir(self):
504
"""Retrieve the controldir format to use for checkouts of this one.
506
medium = self._client._medium
507
if medium._is_remote_before((2, 5)):
508
return self._vfs_checkout_metadir()
509
path = self._path_for_remote_call(self._client)
511
response = self._client.call('BzrDir.checkout_metadir',
513
except errors.UnknownSmartMethod:
514
medium._remember_remote_is_before((2, 5))
515
return self._vfs_checkout_metadir()
516
if len(response) != 3:
517
raise errors.UnexpectedSmartServerResponse(response)
518
control_name, repo_name, branch_name = response
520
format = controldir.network_format_registry.get(control_name)
522
raise errors.UnknownFormatError(kind='control',
526
repo_format = _mod_repository.network_format_registry.get(
529
raise errors.UnknownFormatError(kind='repository',
531
format.repository_format = repo_format
534
format.set_branch_format(
535
branch.network_format_registry.get(branch_name))
537
raise errors.UnknownFormatError(kind='branch',
541
def _vfs_cloning_metadir(self, require_stacking=False):
543
return self._real_bzrdir.cloning_metadir(
544
require_stacking=require_stacking)
546
def cloning_metadir(self, require_stacking=False):
547
medium = self._client._medium
548
if medium._is_remote_before((1, 13)):
549
return self._vfs_cloning_metadir(require_stacking=require_stacking)
550
verb = 'BzrDir.cloning_metadir'
555
path = self._path_for_remote_call(self._client)
557
response = self._call(verb, path, stacking)
558
except errors.UnknownSmartMethod:
559
medium._remember_remote_is_before((1, 13))
560
return self._vfs_cloning_metadir(require_stacking=require_stacking)
561
except errors.UnknownErrorFromSmartServer, err:
562
if err.error_tuple != ('BranchReference',):
564
# We need to resolve the branch reference to determine the
565
# cloning_metadir. This causes unnecessary RPCs to open the
566
# referenced branch (and bzrdir, etc) but only when the caller
567
# didn't already resolve the branch reference.
568
referenced_branch = self.open_branch()
569
return referenced_branch.bzrdir.cloning_metadir()
570
if len(response) != 3:
571
raise errors.UnexpectedSmartServerResponse(response)
572
control_name, repo_name, branch_info = response
573
if len(branch_info) != 2:
574
raise errors.UnexpectedSmartServerResponse(response)
575
branch_ref, branch_name = branch_info
577
format = controldir.network_format_registry.get(control_name)
579
raise errors.UnknownFormatError(kind='control', format=control_name)
583
format.repository_format = _mod_repository.network_format_registry.get(
586
raise errors.UnknownFormatError(kind='repository',
588
if branch_ref == 'ref':
589
# XXX: we need possible_transports here to avoid reopening the
590
# connection to the referenced location
591
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
592
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
593
format.set_branch_format(branch_format)
594
elif branch_ref == 'branch':
597
branch_format = branch.network_format_registry.get(
600
raise errors.UnknownFormatError(kind='branch',
602
format.set_branch_format(branch_format)
604
raise errors.UnexpectedSmartServerResponse(response)
111
def cloning_metadir(self, stacked=False):
113
return self._real_bzrdir.cloning_metadir(stacked)
607
115
def create_repository(self, shared=False):
608
# as per meta1 formats - just delegate to the format object which may
610
result = self._format.repository_format.initialize(self, shared)
611
if not isinstance(result, RemoteRepository):
612
return self.open_repository()
117
self._real_bzrdir.create_repository(shared=shared)
118
return self.open_repository()
616
120
def destroy_repository(self):
617
121
"""See BzrDir.destroy_repository"""
618
path = self._path_for_remote_call(self._client)
620
response = self._call('BzrDir.destroy_repository', path)
621
except errors.UnknownSmartMethod:
623
self._real_bzrdir.destroy_repository()
625
if response[0] != 'ok':
626
raise SmartProtocolError('unexpected response code %s' % (response,))
628
def create_branch(self, name=None, repository=None,
629
append_revisions_only=None):
631
name = self._get_selected_branch()
633
raise errors.NoColocatedBranchSupport(self)
634
# as per meta1 formats - just delegate to the format object which may
636
real_branch = self._format.get_branch_format().initialize(self,
637
name=name, repository=repository,
638
append_revisions_only=append_revisions_only)
639
if not isinstance(real_branch, RemoteBranch):
640
if not isinstance(repository, RemoteRepository):
641
raise AssertionError(
642
'need a RemoteRepository to use with RemoteBranch, got %r'
644
result = RemoteBranch(self, repository, real_branch, name=name)
647
# BzrDir.clone_on_transport() uses the result of create_branch but does
648
# not return it to its callers; we save approximately 8% of our round
649
# trips by handing the branch we created back to the first caller to
650
# open_branch rather than probing anew. Long term we need a API in
651
# bzrdir that doesn't discard result objects (like result_branch).
653
self._next_open_branch_result = result
656
def destroy_branch(self, name=None):
123
self._real_bzrdir.destroy_repository()
125
def create_branch(self):
127
real_branch = self._real_bzrdir.create_branch()
128
return RemoteBranch(self, self.find_repository(), real_branch)
130
def destroy_branch(self):
657
131
"""See BzrDir.destroy_branch"""
659
name = self._get_selected_branch()
661
raise errors.NoColocatedBranchSupport(self)
662
path = self._path_for_remote_call(self._client)
668
response = self._call('BzrDir.destroy_branch', path, *args)
669
except errors.UnknownSmartMethod:
671
self._real_bzrdir.destroy_branch(name=name)
672
self._next_open_branch_result = None
674
self._next_open_branch_result = None
675
if response[0] != 'ok':
676
raise SmartProtocolError('unexpected response code %s' % (response,))
133
self._real_bzrdir.destroy_branch()
678
def create_workingtree(self, revision_id=None, from_branch=None,
679
accelerator_tree=None, hardlink=False):
135
def create_workingtree(self, revision_id=None, from_branch=None):
680
136
raise errors.NotLocalUrl(self.transport.base)
682
def find_branch_format(self, name=None):
138
def find_branch_format(self):
683
139
"""Find the branch 'format' for this bzrdir.
685
141
This might be a synthetic object for e.g. RemoteBranch and SVN.
687
b = self.open_branch(name=name)
143
b = self.open_branch()
690
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
691
path = self._path_for_remote_call(self._client)
693
response, handler = self._call_expecting_body(
694
'BzrDir.get_branches', path)
695
except errors.UnknownSmartMethod:
697
return self._real_bzrdir.get_branches()
698
if response[0] != "success":
699
raise errors.UnexpectedSmartServerResponse(response)
700
body = bencode.bdecode(handler.read_body_bytes())
702
for (name, value) in body.iteritems():
703
ret[name] = self._open_branch(name, value[0], value[1],
704
possible_transports=possible_transports,
705
ignore_fallbacks=ignore_fallbacks)
708
def set_branch_reference(self, target_branch, name=None):
709
"""See BzrDir.set_branch_reference()."""
711
name = self._get_selected_branch()
713
raise errors.NoColocatedBranchSupport(self)
715
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
717
def get_branch_reference(self, name=None):
146
def get_branch_reference(self):
718
147
"""See BzrDir.get_branch_reference()."""
720
name = self._get_selected_branch()
722
raise errors.NoColocatedBranchSupport(self)
723
response = self._get_branch_reference()
724
if response[0] == 'ref':
148
path = self._path_for_remote_call(self._client)
149
response = self._call('BzrDir.open_branch', path)
150
if response[0] == 'ok':
151
if response[1] == '':
152
# branch at this location.
155
# a branch reference, use the existing BranchReference logic.
729
def _get_branch_reference(self):
730
path = self._path_for_remote_call(self._client)
731
medium = self._client._medium
733
('BzrDir.open_branchV3', (2, 1)),
734
('BzrDir.open_branchV2', (1, 13)),
735
('BzrDir.open_branch', None),
737
for verb, required_version in candidate_calls:
738
if required_version and medium._is_remote_before(required_version):
741
response = self._call(verb, path)
742
except errors.UnknownSmartMethod:
743
if required_version is None:
745
medium._remember_remote_is_before(required_version)
748
if verb == 'BzrDir.open_branch':
749
if response[0] != 'ok':
750
raise errors.UnexpectedSmartServerResponse(response)
751
if response[1] != '':
752
return ('ref', response[1])
754
return ('branch', '')
755
if response[0] not in ('ref', 'branch'):
756
158
raise errors.UnexpectedSmartServerResponse(response)
759
def _get_tree_branch(self, name=None):
160
def _get_tree_branch(self):
760
161
"""See BzrDir._get_tree_branch()."""
761
return None, self.open_branch(name=name)
162
return None, self.open_branch()
763
def _open_branch(self, name, kind, location_or_format,
764
ignore_fallbacks=False, possible_transports=None):
164
def open_branch(self, _unsupported=False):
166
raise NotImplementedError('unsupported flag support not implemented yet.')
167
reference_url = self.get_branch_reference()
168
if reference_url is None:
169
# branch at this location.
170
return RemoteBranch(self, self.find_repository())
766
172
# a branch reference, use the existing BranchReference logic.
767
173
format = BranchReferenceFormat()
768
return format.open(self, name=name, _found=True,
769
location=location_or_format, ignore_fallbacks=ignore_fallbacks,
770
possible_transports=possible_transports)
771
branch_format_name = location_or_format
772
if not branch_format_name:
773
branch_format_name = None
774
format = RemoteBranchFormat(network_name=branch_format_name)
775
return RemoteBranch(self, self.find_repository(), format=format,
776
setup_stacking=not ignore_fallbacks, name=name,
777
possible_transports=possible_transports)
779
def open_branch(self, name=None, unsupported=False,
780
ignore_fallbacks=False, possible_transports=None):
782
name = self._get_selected_branch()
784
raise errors.NoColocatedBranchSupport(self)
786
raise NotImplementedError('unsupported flag support not implemented yet.')
787
if self._next_open_branch_result is not None:
788
# See create_branch for details.
789
result = self._next_open_branch_result
790
self._next_open_branch_result = None
792
response = self._get_branch_reference()
793
return self._open_branch(name, response[0], response[1],
794
possible_transports=possible_transports,
795
ignore_fallbacks=ignore_fallbacks)
797
def _open_repo_v1(self, path):
798
verb = 'BzrDir.find_repository'
799
response = self._call(verb, path)
800
if response[0] != 'ok':
801
raise errors.UnexpectedSmartServerResponse(response)
802
# servers that only support the v1 method don't support external
805
repo = self._real_bzrdir.open_repository()
806
response = response + ('no', repo._format.network_name())
807
return response, repo
809
def _open_repo_v2(self, path):
174
return format.open(self, _found=True, location=reference_url)
176
def open_repository(self):
177
path = self._path_for_remote_call(self._client)
810
178
verb = 'BzrDir.find_repositoryV2'
811
response = self._call(verb, path)
812
if response[0] != 'ok':
813
raise errors.UnexpectedSmartServerResponse(response)
815
repo = self._real_bzrdir.open_repository()
816
response = response + (repo._format.network_name(),)
817
return response, repo
819
def _open_repo_v3(self, path):
820
verb = 'BzrDir.find_repositoryV3'
821
medium = self._client._medium
822
if medium._is_remote_before((1, 13)):
823
raise errors.UnknownSmartMethod(verb)
825
180
response = self._call(verb, path)
826
181
except errors.UnknownSmartMethod:
827
medium._remember_remote_is_before((1, 13))
829
if response[0] != 'ok':
830
raise errors.UnexpectedSmartServerResponse(response)
831
return response, None
833
def open_repository(self):
834
path = self._path_for_remote_call(self._client)
836
for probe in [self._open_repo_v3, self._open_repo_v2,
839
response, real_repo = probe(path)
841
except errors.UnknownSmartMethod:
844
raise errors.UnknownSmartMethod('BzrDir.find_repository{3,2,}')
845
if response[0] != 'ok':
846
raise errors.UnexpectedSmartServerResponse(response)
847
if len(response) != 6:
182
verb = 'BzrDir.find_repository'
183
response = self._call(verb, path)
184
if response[0] != 'ok':
185
raise errors.UnexpectedSmartServerResponse(response)
186
if verb == 'BzrDir.find_repository':
187
# servers that don't support the V2 method don't support external
189
response = response + ('no', )
190
if not (len(response) == 5):
848
191
raise SmartProtocolError('incorrect response length %s' % (response,))
849
192
if response[1] == '':
850
# repo is at this dir.
851
format = response_tuple_to_repo_format(response[2:])
193
format = RemoteRepositoryFormat()
194
format.rich_root_data = (response[2] == 'yes')
195
format.supports_tree_reference = (response[3] == 'yes')
196
# No wire format to check this yet.
197
format.supports_external_lookups = (response[4] == 'yes')
852
198
# Used to support creating a real format instance when needed.
853
199
format._creating_bzrdir = self
854
remote_repo = RemoteRepository(self, format)
855
format._creating_repo = remote_repo
856
if real_repo is not None:
857
remote_repo._set_real_repository(real_repo)
200
return RemoteRepository(self, format)
860
202
raise errors.NoRepositoryPresent(self)
862
def has_workingtree(self):
863
if self._has_working_tree is None:
864
path = self._path_for_remote_call(self._client)
866
response = self._call('BzrDir.has_workingtree', path)
867
except errors.UnknownSmartMethod:
869
self._has_working_tree = self._real_bzrdir.has_workingtree()
871
if response[0] not in ('yes', 'no'):
872
raise SmartProtocolError('unexpected response code %s' % (response,))
873
self._has_working_tree = (response[0] == 'yes')
874
return self._has_working_tree
876
204
def open_workingtree(self, recommend_upgrade=True):
877
if self.has_workingtree():
206
if self._real_bzrdir.has_workingtree():
878
207
raise errors.NotLocalUrl(self.root_transport)
880
209
raise errors.NoWorkingTree(self.root_transport.base)
882
211
def _path_for_remote_call(self, client):
883
212
"""Return the path to be used for this bzrdir in a remote call."""
884
return urlutils.split_segment_parameters_raw(
885
client.remote_path_from_transport(self.root_transport))[0]
213
return client.remote_path_from_transport(self.root_transport)
887
def get_branch_transport(self, branch_format, name=None):
215
def get_branch_transport(self, branch_format):
888
216
self._ensure_real()
889
return self._real_bzrdir.get_branch_transport(branch_format, name=name)
217
return self._real_bzrdir.get_branch_transport(branch_format)
891
219
def get_repository_transport(self, repository_format):
892
220
self._ensure_real()
922
254
the attributes rich_root_data and supports_tree_reference are set
923
255
on a per instance basis, and are not set (and should not be) at
926
:ivar _custom_format: If set, a specific concrete repository format that
927
will be used when initializing a repository with this
928
RemoteRepositoryFormat.
929
:ivar _creating_repo: If set, the repository object that this
930
RemoteRepositoryFormat was created for: it can be called into
931
to obtain data like the network name.
934
259
_matchingbzrdir = RemoteBzrDirFormat()
935
supports_full_versioned_files = True
936
supports_leaving_lock = True
939
_mod_repository.RepositoryFormat.__init__(self)
940
self._custom_format = None
941
self._network_name = None
942
self._creating_bzrdir = None
943
self._revision_graph_can_have_wrong_parents = None
944
self._supports_chks = None
945
self._supports_external_lookups = None
946
self._supports_tree_reference = None
947
self._supports_funky_characters = None
948
self._supports_nesting_repositories = None
949
self._rich_root_data = None
952
return "%s(_network_name=%r)" % (self.__class__.__name__,
956
def fast_deltas(self):
958
return self._custom_format.fast_deltas
961
def rich_root_data(self):
962
if self._rich_root_data is None:
964
self._rich_root_data = self._custom_format.rich_root_data
965
return self._rich_root_data
968
def supports_chks(self):
969
if self._supports_chks is None:
971
self._supports_chks = self._custom_format.supports_chks
972
return self._supports_chks
975
def supports_external_lookups(self):
976
if self._supports_external_lookups is None:
978
self._supports_external_lookups = \
979
self._custom_format.supports_external_lookups
980
return self._supports_external_lookups
983
def supports_funky_characters(self):
984
if self._supports_funky_characters is None:
986
self._supports_funky_characters = \
987
self._custom_format.supports_funky_characters
988
return self._supports_funky_characters
991
def supports_nesting_repositories(self):
992
if self._supports_nesting_repositories is None:
994
self._supports_nesting_repositories = \
995
self._custom_format.supports_nesting_repositories
996
return self._supports_nesting_repositories
999
def supports_tree_reference(self):
1000
if self._supports_tree_reference is None:
1002
self._supports_tree_reference = \
1003
self._custom_format.supports_tree_reference
1004
return self._supports_tree_reference
1007
def revision_graph_can_have_wrong_parents(self):
1008
if self._revision_graph_can_have_wrong_parents is None:
1010
self._revision_graph_can_have_wrong_parents = \
1011
self._custom_format.revision_graph_can_have_wrong_parents
1012
return self._revision_graph_can_have_wrong_parents
1014
def _vfs_initialize(self, a_bzrdir, shared):
1015
"""Helper for common code in initialize."""
1016
if self._custom_format:
1017
# Custom format requested
1018
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1019
elif self._creating_bzrdir is not None:
1020
# Use the format that the repository we were created to back
261
def initialize(self, a_bzrdir, shared=False):
262
if not isinstance(a_bzrdir, RemoteBzrDir):
1022
263
prior_repo = self._creating_bzrdir.open_repository()
1023
264
prior_repo._ensure_real()
1024
result = prior_repo._real_repository._format.initialize(
265
return prior_repo._real_repository._format.initialize(
1025
266
a_bzrdir, shared=shared)
1027
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1028
# support remote initialization.
1029
# We delegate to a real object at this point (as RemoteBzrDir
1030
# delegate to the repository format which would lead to infinite
1031
# recursion if we just called a_bzrdir.create_repository.
1032
a_bzrdir._ensure_real()
1033
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1034
if not isinstance(result, RemoteRepository):
1035
return self.open(a_bzrdir)
1039
def initialize(self, a_bzrdir, shared=False):
1040
# Being asked to create on a non RemoteBzrDir:
1041
if not isinstance(a_bzrdir, RemoteBzrDir):
1042
return self._vfs_initialize(a_bzrdir, shared)
1043
medium = a_bzrdir._client._medium
1044
if medium._is_remote_before((1, 13)):
1045
return self._vfs_initialize(a_bzrdir, shared)
1046
# Creating on a remote bzr dir.
1047
# 1) get the network name to use.
1048
if self._custom_format:
1049
network_name = self._custom_format.network_name()
1050
elif self._network_name:
1051
network_name = self._network_name
1053
# Select the current bzrlib default and ask for that.
1054
reference_bzrdir_format = controldir.format_registry.get('default')()
1055
reference_format = reference_bzrdir_format.repository_format
1056
network_name = reference_format.network_name()
1057
# 2) try direct creation via RPC
1058
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
1059
verb = 'BzrDir.create_repository'
1063
shared_str = 'False'
1065
response = a_bzrdir._call(verb, path, network_name, shared_str)
1066
except errors.UnknownSmartMethod:
1067
# Fallback - use vfs methods
1068
medium._remember_remote_is_before((1, 13))
1069
return self._vfs_initialize(a_bzrdir, shared)
1071
# Turn the response into a RemoteRepository object.
1072
format = response_tuple_to_repo_format(response[1:])
1073
# Used to support creating a real format instance when needed.
1074
format._creating_bzrdir = a_bzrdir
1075
remote_repo = RemoteRepository(a_bzrdir, format)
1076
format._creating_repo = remote_repo
267
return a_bzrdir.create_repository(shared=shared)
1079
269
def open(self, a_bzrdir):
1080
270
if not isinstance(a_bzrdir, RemoteBzrDir):
1081
271
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
1082
272
return a_bzrdir.open_repository()
1084
def _ensure_real(self):
1085
if self._custom_format is None:
1087
self._custom_format = _mod_repository.network_format_registry.get(
1090
raise errors.UnknownFormatError(kind='repository',
1091
format=self._network_name)
1094
def _fetch_order(self):
1096
return self._custom_format._fetch_order
1099
def _fetch_uses_deltas(self):
1101
return self._custom_format._fetch_uses_deltas
1104
def _fetch_reconcile(self):
1106
return self._custom_format._fetch_reconcile
1108
274
def get_format_description(self):
1110
return 'Remote: ' + self._custom_format.get_format_description()
275
return 'bzr remote repository'
1112
277
def __eq__(self, other):
1113
return self.__class__ is other.__class__
1115
def network_name(self):
1116
if self._network_name:
1117
return self._network_name
1118
self._creating_repo._ensure_real()
1119
return self._creating_repo._real_repository._format.network_name()
1122
def pack_compresses(self):
1124
return self._custom_format.pack_compresses
1127
def _serializer(self):
1129
return self._custom_format._serializer
1132
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
1133
lock._RelockDebugMixin):
278
return self.__class__ == other.__class__
280
def check_conversion_target(self, target_format):
281
if self.rich_root_data and not target_format.rich_root_data:
282
raise errors.BadConversionTarget(
283
'Does not support rich root data.', target_format)
284
if (self.supports_tree_reference and
285
not getattr(target_format, 'supports_tree_reference', False)):
286
raise errors.BadConversionTarget(
287
'Does not support nested trees', target_format)
290
class RemoteRepository(_RpcHelper):
1134
291
"""Repository accessed over rpc.
1136
293
For the moment most operations are performed using local transport-backed
1199
342
def abort_write_group(self, suppress_errors=False):
1200
343
"""Complete a write group on the decorated repository.
1202
Smart methods perform operations in a single step so this API
345
Smart methods peform operations in a single step so this api
1203
346
is not really applicable except as a compatibility thunk
1204
347
for older plugins that don't use e.g. the CommitBuilder
1207
350
:param suppress_errors: see Repository.abort_write_group.
1209
if self._real_repository:
1211
return self._real_repository.abort_write_group(
1212
suppress_errors=suppress_errors)
1213
if not self.is_in_write_group():
1215
mutter('(suppressed) not in write group')
1217
raise errors.BzrError("not in write group")
1218
path = self.bzrdir._path_for_remote_call(self._client)
1220
response = self._call('Repository.abort_write_group', path,
1221
self._lock_token, self._write_group_tokens)
1222
except Exception, exc:
1223
self._write_group = None
1224
if not suppress_errors:
1226
mutter('abort_write_group failed')
1227
log_exception_quietly()
1228
note(gettext('bzr: ERROR (ignored): %s'), exc)
1230
if response != ('ok', ):
1231
raise errors.UnexpectedSmartServerResponse(response)
1232
self._write_group_tokens = None
1235
def chk_bytes(self):
1236
"""Decorate the real repository for now.
1238
In the long term a full blown network facility is needed to avoid
1239
creating a real repository object locally.
1241
352
self._ensure_real()
1242
return self._real_repository.chk_bytes
353
return self._real_repository.abort_write_group(
354
suppress_errors=suppress_errors)
1244
356
def commit_write_group(self):
1245
357
"""Complete a write group on the decorated repository.
1247
Smart methods perform operations in a single step so this API
359
Smart methods peform operations in a single step so this api
1248
360
is not really applicable except as a compatibility thunk
1249
361
for older plugins that don't use e.g. the CommitBuilder
1252
if self._real_repository:
1254
return self._real_repository.commit_write_group()
1255
if not self.is_in_write_group():
1256
raise errors.BzrError("not in write group")
1257
path = self.bzrdir._path_for_remote_call(self._client)
1258
response = self._call('Repository.commit_write_group', path,
1259
self._lock_token, self._write_group_tokens)
1260
if response != ('ok', ):
1261
raise errors.UnexpectedSmartServerResponse(response)
1262
self._write_group_tokens = None
1263
# Refresh data after writing to the repository.
1266
def resume_write_group(self, tokens):
1267
if self._real_repository:
1268
return self._real_repository.resume_write_group(tokens)
1269
path = self.bzrdir._path_for_remote_call(self._client)
1271
response = self._call('Repository.check_write_group', path,
1272
self._lock_token, tokens)
1273
except errors.UnknownSmartMethod:
1275
return self._real_repository.resume_write_group(tokens)
1276
if response != ('ok', ):
1277
raise errors.UnexpectedSmartServerResponse(response)
1278
self._write_group_tokens = tokens
1280
def suspend_write_group(self):
1281
if self._real_repository:
1282
return self._real_repository.suspend_write_group()
1283
ret = self._write_group_tokens or []
1284
self._write_group_tokens = None
1287
def get_missing_parent_inventories(self, check_for_missing_texts=True):
1289
return self._real_repository.get_missing_parent_inventories(
1290
check_for_missing_texts=check_for_missing_texts)
1292
def _get_rev_id_for_revno_vfs(self, revno, known_pair):
1294
return self._real_repository.get_rev_id_for_revno(
1297
def get_rev_id_for_revno(self, revno, known_pair):
1298
"""See Repository.get_rev_id_for_revno."""
1299
path = self.bzrdir._path_for_remote_call(self._client)
1301
if self._client._medium._is_remote_before((1, 17)):
1302
return self._get_rev_id_for_revno_vfs(revno, known_pair)
1303
response = self._call(
1304
'Repository.get_rev_id_for_revno', path, revno, known_pair)
1305
except errors.UnknownSmartMethod:
1306
self._client._medium._remember_remote_is_before((1, 17))
1307
return self._get_rev_id_for_revno_vfs(revno, known_pair)
1308
if response[0] == 'ok':
1309
return True, response[1]
1310
elif response[0] == 'history-incomplete':
1311
known_pair = response[1:3]
1312
for fallback in self._fallback_repositories:
1313
found, result = fallback.get_rev_id_for_revno(revno, known_pair)
1318
# Not found in any fallbacks
1319
return False, known_pair
1321
raise errors.UnexpectedSmartServerResponse(response)
365
return self._real_repository.commit_write_group()
1323
367
def _ensure_real(self):
1324
368
"""Ensure that there is a _real_repository set.
1326
370
Used before calls to self._real_repository.
1328
Note that _ensure_real causes many roundtrips to the server which are
1329
not desirable, and prevents the use of smart one-roundtrip RPC's to
1330
perform complex operations (such as accessing parent data, streaming
1331
revisions etc). Adding calls to _ensure_real should only be done when
1332
bringing up new functionality, adding fallbacks for smart methods that
1333
require a fallback path, and never to replace an existing smart method
1334
invocation. If in doubt chat to the bzr network team.
1336
372
if self._real_repository is None:
1337
if 'hpssvfs' in debug.debug_flags:
1339
warning('VFS Repository access triggered\n%s',
1340
''.join(traceback.format_stack()))
1341
self._unstacked_provider.missing_keys.clear()
1342
373
self.bzrdir._ensure_real()
1343
374
self._set_real_repository(
1344
375
self.bzrdir._real_bzrdir.open_repository())
1768
692
raise errors.UnexpectedSmartServerResponse(response)
1771
694
def sprout(self, to_bzrdir, revision_id=None):
1772
"""Create a descendent repository for new development.
1774
Unlike clone, this does not copy the settings of the repository.
1776
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
695
# TODO: Option to control what format is created?
697
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1777
699
dest_repo.fetch(self, revision_id=revision_id)
1778
700
return dest_repo
1780
def _create_sprouting_repo(self, a_bzrdir, shared):
1781
if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
1782
# use target default format.
1783
dest_repo = a_bzrdir.create_repository()
1785
# Most control formats need the repository to be specifically
1786
# created, but on some old all-in-one formats it's not needed
1788
dest_repo = self._format.initialize(a_bzrdir, shared=shared)
1789
except errors.UninitializableFormat:
1790
dest_repo = a_bzrdir.open_repository()
1793
702
### These methods are just thin shims to the VFS object for now.
1796
704
def revision_tree(self, revision_id):
1797
revision_id = _mod_revision.ensure_null(revision_id)
1798
if revision_id == _mod_revision.NULL_REVISION:
1799
return InventoryRevisionTree(self,
1800
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1802
return list(self.revision_trees([revision_id]))[0]
706
return self._real_repository.revision_tree(revision_id)
1804
708
def get_serializer_format(self):
1805
path = self.bzrdir._path_for_remote_call(self._client)
1807
response = self._call('VersionedFileRepository.get_serializer_format',
1809
except errors.UnknownSmartMethod:
1811
return self._real_repository.get_serializer_format()
1812
if response[0] != 'ok':
1813
raise errors.UnexpectedSmartServerResponse(response)
710
return self._real_repository.get_serializer_format()
1816
712
def get_commit_builder(self, branch, parents, config, timestamp=None,
1817
713
timezone=None, committer=None, revprops=None,
1818
revision_id=None, lossy=False):
1819
"""Obtain a CommitBuilder for this repository.
1821
:param branch: Branch to commit to.
1822
:param parents: Revision ids of the parents of the new revision.
1823
:param config: Configuration to use.
1824
:param timestamp: Optional timestamp recorded for commit.
1825
:param timezone: Optional timezone for timestamp.
1826
:param committer: Optional committer to set for commit.
1827
:param revprops: Optional dictionary of revision properties.
1828
:param revision_id: Optional revision id.
1829
:param lossy: Whether to discard data that can not be natively
1830
represented, when pushing to a foreign VCS
1832
if self._fallback_repositories and not self._format.supports_chks:
1833
raise errors.BzrError("Cannot commit directly to a stacked branch"
1834
" in pre-2a formats. See "
1835
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1836
if self._format.rich_root_data:
1837
commit_builder_kls = vf_repository.VersionedFileRootCommitBuilder
1839
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1840
result = commit_builder_kls(self, parents, config,
1841
timestamp, timezone, committer, revprops, revision_id,
1843
self.start_write_group()
715
# FIXME: It ought to be possible to call this without immediately
716
# triggering _ensure_real. For now it's the easiest thing to do.
718
real_repo = self._real_repository
719
builder = real_repo.get_commit_builder(branch, parents,
720
config, timestamp=timestamp, timezone=timezone,
721
committer=committer, revprops=revprops, revision_id=revision_id)
1846
724
def add_fallback_repository(self, repository):
1847
725
"""Add a repository to use for looking up data not held locally.
1849
727
:param repository: A repository.
1851
if not self._format.supports_external_lookups:
1852
raise errors.UnstackableRepositoryFormat(
1853
self._format.network_name(), self.base)
729
# XXX: At the moment the RemoteRepository will allow fallbacks
730
# unconditionally - however, a _real_repository will usually exist,
731
# and may raise an error if it's not accommodated by the underlying
732
# format. Eventually we should check when opening the repository
733
# whether it's willing to allow them or not.
1854
735
# We need to accumulate additional repositories here, to pass them in
1855
736
# on various RPC's.
1857
# Make the check before we lock: this raises an exception.
1858
self._check_fallback_repository(repository)
1859
if self.is_locked():
1860
# We will call fallback.unlock() when we transition to the unlocked
1861
# state, so always add a lock here. If a caller passes us a locked
1862
# repository, they are responsible for unlocking it later.
1863
repository.lock_read()
1864
737
self._fallback_repositories.append(repository)
1865
# If self._real_repository was parameterised already (e.g. because a
1866
# _real_branch had its get_stacked_on_url method called), then the
1867
# repository to be added may already be in the _real_repositories list.
1868
if self._real_repository is not None:
1869
fallback_locations = [repo.user_url for repo in
1870
self._real_repository._fallback_repositories]
1871
if repository.user_url not in fallback_locations:
1872
self._real_repository.add_fallback_repository(repository)
1874
def _check_fallback_repository(self, repository):
1875
"""Check that this repository can fallback to repository safely.
1877
Raise an error if not.
1879
:param repository: A repository to fallback to.
1881
return _mod_repository.InterRepository._assert_same_model(
738
# They are also seen by the fallback repository. If it doesn't exist
739
# yet they'll be added then. This implicitly copies them.
1884
742
def add_inventory(self, revid, inv, parents):
1885
743
self._ensure_real()
1886
744
return self._real_repository.add_inventory(revid, inv, parents)
1888
746
def add_inventory_by_delta(self, basis_revision_id, delta, new_revision_id,
1889
parents, basis_inv=None, propagate_caches=False):
1890
748
self._ensure_real()
1891
749
return self._real_repository.add_inventory_by_delta(basis_revision_id,
1892
delta, new_revision_id, parents, basis_inv=basis_inv,
1893
propagate_caches=propagate_caches)
1895
def add_revision(self, revision_id, rev, inv=None):
1896
_mod_revision.check_not_reserved_id(revision_id)
1897
key = (revision_id,)
1898
# check inventory present
1899
if not self.inventories.get_parent_map([key]):
1901
raise errors.WeaveRevisionNotPresent(revision_id,
1904
# yes, this is not suitable for adding with ghosts.
1905
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1908
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1909
self._add_revision(rev)
1911
def _add_revision(self, rev):
1912
if self._real_repository is not None:
1913
return self._real_repository._add_revision(rev)
1914
text = self._serializer.write_revision_to_string(rev)
1915
key = (rev.revision_id,)
1916
parents = tuple((parent,) for parent in rev.parent_ids)
1917
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1918
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1919
self._format, self._write_group_tokens)
750
delta, new_revision_id, parents)
752
def add_revision(self, rev_id, rev, inv=None, config=None):
754
return self._real_repository.add_revision(
755
rev_id, rev, inv=inv, config=config)
1921
757
@needs_read_lock
1922
758
def get_inventory(self, revision_id):
1923
return list(self.iter_inventories([revision_id]))[0]
1925
def _iter_inventories_rpc(self, revision_ids, ordering):
1926
if ordering is None:
1927
ordering = 'unordered'
1928
path = self.bzrdir._path_for_remote_call(self._client)
1929
body = "\n".join(revision_ids)
1930
response_tuple, response_handler = (
1931
self._call_with_body_bytes_expecting_body(
1932
"VersionedFileRepository.get_inventories",
1933
(path, ordering), body))
1934
if response_tuple[0] != "ok":
1935
raise errors.UnexpectedSmartServerResponse(response_tuple)
1936
deserializer = inventory_delta.InventoryDeltaDeserializer()
1937
byte_stream = response_handler.read_streamed_body()
1938
decoded = smart_repo._byte_stream_to_stream(byte_stream)
1940
# no results whatsoever
1942
src_format, stream = decoded
1943
if src_format.network_name() != self._format.network_name():
1944
raise AssertionError(
1945
"Mismatched RemoteRepository and stream src %r, %r" % (
1946
src_format.network_name(), self._format.network_name()))
1947
# ignore the src format, it's not really relevant
1948
prev_inv = Inventory(root_id=None,
1949
revision_id=_mod_revision.NULL_REVISION)
1950
# there should be just one substream, with inventory deltas
1951
substream_kind, substream = stream.next()
1952
if substream_kind != "inventory-deltas":
1953
raise AssertionError(
1954
"Unexpected stream %r received" % substream_kind)
1955
for record in substream:
1956
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
1957
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
1958
if parent_id != prev_inv.revision_id:
1959
raise AssertionError("invalid base %r != %r" % (parent_id,
1960
prev_inv.revision_id))
1961
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
1962
yield inv, inv.revision_id
1965
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1967
return self._real_repository._iter_inventories(revision_ids, ordering)
1969
def iter_inventories(self, revision_ids, ordering=None):
1970
"""Get many inventories by revision_ids.
1972
This will buffer some or all of the texts used in constructing the
1973
inventories in memory, but will only parse a single inventory at a
1976
:param revision_ids: The expected revision ids of the inventories.
1977
:param ordering: optional ordering, e.g. 'topological'. If not
1978
specified, the order of revision_ids will be preserved (by
1979
buffering if necessary).
1980
:return: An iterator of inventories.
1982
if ((None in revision_ids)
1983
or (_mod_revision.NULL_REVISION in revision_ids)):
1984
raise ValueError('cannot get null revision inventory')
1985
for inv, revid in self._iter_inventories(revision_ids, ordering):
1987
raise errors.NoSuchRevision(self, revid)
1990
def _iter_inventories(self, revision_ids, ordering=None):
1991
if len(revision_ids) == 0:
1993
missing = set(revision_ids)
1994
if ordering is None:
1995
order_as_requested = True
1997
order = list(revision_ids)
1999
next_revid = order.pop()
2001
order_as_requested = False
2002
if ordering != 'unordered' and self._fallback_repositories:
2003
raise ValueError('unsupported ordering %r' % ordering)
2004
iter_inv_fns = [self._iter_inventories_rpc] + [
2005
fallback._iter_inventories for fallback in
2006
self._fallback_repositories]
2008
for iter_inv in iter_inv_fns:
2009
request = [revid for revid in revision_ids if revid in missing]
2010
for inv, revid in iter_inv(request, ordering):
2013
missing.remove(inv.revision_id)
2014
if ordering != 'unordered':
2018
if order_as_requested:
2019
# Yield as many results as we can while preserving order.
2020
while next_revid in invs:
2021
inv = invs.pop(next_revid)
2022
yield inv, inv.revision_id
2024
next_revid = order.pop()
2026
# We still want to fully consume the stream, just
2027
# in case it is not actually finished at this point
2030
except errors.UnknownSmartMethod:
2031
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2035
if order_as_requested:
2036
if next_revid is not None:
2037
yield None, next_revid
2040
yield invs.get(revid), revid
2043
yield None, missing.pop()
760
return self._real_repository.get_inventory(revision_id)
762
def iter_inventories(self, revision_ids):
764
return self._real_repository.iter_inventories(revision_ids)
2045
766
@needs_read_lock
2046
767
def get_revision(self, revision_id):
2047
return self.get_revisions([revision_id])[0]
769
return self._real_repository.get_revision(revision_id)
2049
771
def get_transaction(self):
2050
772
self._ensure_real()
2095
791
included_keys = result_set.intersection(result_parents)
2096
792
start_keys = result_set.difference(included_keys)
2097
793
exclude_keys = result_parents.difference(result_set)
2098
result = vf_search.SearchResult(start_keys, exclude_keys,
794
result = graph.SearchResult(start_keys, exclude_keys,
2099
795
len(result_set), result_set)
2102
798
@needs_read_lock
2103
def search_missing_revision_ids(self, other,
2104
revision_id=symbol_versioning.DEPRECATED_PARAMETER,
2105
find_ghosts=True, revision_ids=None, if_present_ids=None,
799
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2107
800
"""Return the revision ids that other has that this does not.
2109
802
These are returned in topological order.
2111
804
revision_id: only return revision ids included by revision_id.
2113
if symbol_versioning.deprecated_passed(revision_id):
2114
symbol_versioning.warn(
2115
'search_missing_revision_ids(revision_id=...) was '
2116
'deprecated in 2.4. Use revision_ids=[...] instead.',
2117
DeprecationWarning, stacklevel=2)
2118
if revision_ids is not None:
2119
raise AssertionError(
2120
'revision_ids is mutually exclusive with revision_id')
2121
if revision_id is not None:
2122
revision_ids = [revision_id]
2123
inter_repo = _mod_repository.InterRepository.get(other, self)
2124
return inter_repo.search_missing_revision_ids(
2125
find_ghosts=find_ghosts, revision_ids=revision_ids,
2126
if_present_ids=if_present_ids, limit=limit)
806
return repository.InterRepository.get(
807
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2128
def fetch(self, source, revision_id=None, find_ghosts=False,
2130
# No base implementation to use as RemoteRepository is not a subclass
2131
# of Repository; so this is a copy of Repository.fetch().
2132
if fetch_spec is not None and revision_id is not None:
2133
raise AssertionError(
2134
"fetch_spec and revision_id are mutually exclusive.")
2135
if self.is_in_write_group():
2136
raise errors.InternalBzrError(
2137
"May not fetch while in a write group.")
2138
# fast path same-url fetch operations
2139
if (self.has_same_location(source)
2140
and fetch_spec is None
2141
and self._has_same_fallbacks(source)):
809
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False):
810
# Not delegated to _real_repository so that InterRepository.get has a
811
# chance to find an InterRepository specialised for RemoteRepository.
812
if self.has_same_location(source):
2142
813
# check that last_revision is in 'from' and then return a
2144
815
if (revision_id is not None and
2145
not _mod_revision.is_null(revision_id)):
816
not revision.is_null(revision_id)):
2146
817
self.get_revision(revision_id)
2148
# if there is no specific appropriate InterRepository, this will get
2149
# the InterRepository base class, which raises an
2150
# IncompatibleRepositories when asked to fetch.
2151
inter = _mod_repository.InterRepository.get(source, self)
2152
if (fetch_spec is not None and
2153
not getattr(inter, "supports_fetch_spec", False)):
2154
raise errors.UnsupportedOperation(
2155
"fetch_spec not supported for %r" % inter)
2156
return inter.fetch(revision_id=revision_id,
2157
find_ghosts=find_ghosts, fetch_spec=fetch_spec)
819
inter = repository.InterRepository.get(source, self)
821
return inter.fetch(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts)
822
except NotImplementedError:
823
raise errors.IncompatibleRepositories(source, self)
2159
825
def create_bundle(self, target, base, fileobj, format=None):
2160
826
self._ensure_real()
2161
827
self._real_repository.create_bundle(target, base, fileobj, format)
830
def get_ancestry(self, revision_id, topo_sorted=True):
832
return self._real_repository.get_ancestry(revision_id, topo_sorted)
2163
834
def fileids_altered_by_revision_ids(self, revision_ids):
2164
835
self._ensure_real()
2165
836
return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
2168
839
self._ensure_real()
2169
840
return self._real_repository._get_versioned_file_checker(
2170
841
revisions, revision_versions_cache)
2172
def _iter_files_bytes_rpc(self, desired_files, absent):
2173
path = self.bzrdir._path_for_remote_call(self._client)
2176
for (file_id, revid, identifier) in desired_files:
2177
lines.append("%s\0%s" % (
2178
osutils.safe_file_id(file_id),
2179
osutils.safe_revision_id(revid)))
2180
identifiers.append(identifier)
2181
(response_tuple, response_handler) = (
2182
self._call_with_body_bytes_expecting_body(
2183
"Repository.iter_files_bytes", (path, ), "\n".join(lines)))
2184
if response_tuple != ('ok', ):
2185
response_handler.cancel_read_body()
2186
raise errors.UnexpectedSmartServerResponse(response_tuple)
2187
byte_stream = response_handler.read_streamed_body()
2188
def decompress_stream(start, byte_stream, unused):
2189
decompressor = zlib.decompressobj()
2190
yield decompressor.decompress(start)
2191
while decompressor.unused_data == "":
2193
data = byte_stream.next()
2194
except StopIteration:
2196
yield decompressor.decompress(data)
2197
yield decompressor.flush()
2198
unused.append(decompressor.unused_data)
2201
while not "\n" in unused:
2202
unused += byte_stream.next()
2203
header, rest = unused.split("\n", 1)
2204
args = header.split("\0")
2205
if args[0] == "absent":
2206
absent[identifiers[int(args[3])]] = (args[1], args[2])
2209
elif args[0] == "ok":
2212
raise errors.UnexpectedSmartServerResponse(args)
2214
yield (identifiers[idx],
2215
decompress_stream(rest, byte_stream, unused_chunks))
2216
unused = "".join(unused_chunks)
2218
843
def iter_files_bytes(self, desired_files):
2219
844
"""See Repository.iter_file_bytes.
2223
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2224
desired_files, absent):
2225
yield identifier, bytes_iterator
2226
for fallback in self._fallback_repositories:
2229
desired_files = [(key[0], key[1], identifier) for
2230
(identifier, key) in absent.iteritems()]
2231
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2232
del absent[identifier]
2233
yield identifier, bytes_iterator
2235
# There may be more missing items, but raise an exception
2237
missing_identifier = absent.keys()[0]
2238
missing_key = absent[missing_identifier]
2239
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2240
file_id=missing_key[0])
2241
except errors.UnknownSmartMethod:
2243
for (identifier, bytes_iterator) in (
2244
self._real_repository.iter_files_bytes(desired_files)):
2245
yield identifier, bytes_iterator
2247
def get_cached_parent_map(self, revision_ids):
2248
"""See bzrlib.CachingParentsProvider.get_cached_parent_map"""
2249
return self._unstacked_provider.get_cached_parent_map(revision_ids)
847
return self._real_repository.iter_files_bytes(desired_files)
850
def _fetch_order(self):
851
"""Decorate the real repository for now.
853
In the long term getting this back from the remote repository as part
854
of open would be more efficient.
857
return self._real_repository._fetch_order
860
def _fetch_uses_deltas(self):
861
"""Decorate the real repository for now.
863
In the long term getting this back from the remote repository as part
864
of open would be more efficient.
867
return self._real_repository._fetch_uses_deltas
870
def _fetch_reconcile(self):
871
"""Decorate the real repository for now.
873
In the long term getting this back from the remote repository as part
874
of open would be more efficient.
877
return self._real_repository._fetch_reconcile
2251
879
def get_parent_map(self, revision_ids):
2252
880
"""See bzrlib.Graph.get_parent_map()."""
2363
981
revision_graph[d[0]] = d[1:]
2366
if d[0].startswith('missing:'):
2368
self._unstacked_provider.note_missing_key(revid)
2370
# no parents - so give the Graph result
2372
revision_graph[d[0]] = (NULL_REVISION,)
983
# No parents - so give the Graph result (NULL_REVISION,).
984
revision_graph[d[0]] = (NULL_REVISION,)
2373
985
return revision_graph
2375
987
@needs_read_lock
2376
988
def get_signature_text(self, revision_id):
2377
path = self.bzrdir._path_for_remote_call(self._client)
2379
response_tuple, response_handler = self._call_expecting_body(
2380
'Repository.get_revision_signature_text', path, revision_id)
2381
except errors.UnknownSmartMethod:
2383
return self._real_repository.get_signature_text(revision_id)
2384
except errors.NoSuchRevision, err:
2385
for fallback in self._fallback_repositories:
2387
return fallback.get_signature_text(revision_id)
2388
except errors.NoSuchRevision:
2392
if response_tuple[0] != 'ok':
2393
raise errors.UnexpectedSmartServerResponse(response_tuple)
2394
return response_handler.read_body_bytes()
2397
def _get_inventory_xml(self, revision_id):
2398
# This call is used by older working tree formats,
2399
# which stored a serialized basis inventory.
2401
return self._real_repository._get_inventory_xml(revision_id)
990
return self._real_repository.get_signature_text(revision_id)
993
@symbol_versioning.deprecated_method(symbol_versioning.one_three)
994
def get_revision_graph_with_ghosts(self, revision_ids=None):
996
return self._real_repository.get_revision_graph_with_ghosts(
997
revision_ids=revision_ids)
1000
def get_inventory_xml(self, revision_id):
1002
return self._real_repository.get_inventory_xml(revision_id)
1004
def deserialise_inventory(self, revision_id, xml):
1006
return self._real_repository.deserialise_inventory(revision_id, xml)
2404
1008
def reconcile(self, other=None, thorough=False):
2405
from bzrlib.reconcile import RepoReconciler
2406
path = self.bzrdir._path_for_remote_call(self._client)
2408
response, handler = self._call_expecting_body(
2409
'Repository.reconcile', path, self._lock_token)
2410
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2412
return self._real_repository.reconcile(other=other, thorough=thorough)
2413
if response != ('ok', ):
2414
raise errors.UnexpectedSmartServerResponse(response)
2415
body = handler.read_body_bytes()
2416
result = RepoReconciler(self)
2417
for line in body.split('\n'):
2420
key, val_text = line.split(':')
2421
if key == "garbage_inventories":
2422
result.garbage_inventories = int(val_text)
2423
elif key == "inconsistent_parents":
2424
result.inconsistent_parents = int(val_text)
2426
mutter("unknown reconcile key %r" % key)
1010
return self._real_repository.reconcile(other=other, thorough=thorough)
2429
1012
def all_revision_ids(self):
2430
path = self.bzrdir._path_for_remote_call(self._client)
2432
response_tuple, response_handler = self._call_expecting_body(
2433
"Repository.all_revision_ids", path)
2434
except errors.UnknownSmartMethod:
2436
return self._real_repository.all_revision_ids()
2437
if response_tuple != ("ok", ):
2438
raise errors.UnexpectedSmartServerResponse(response_tuple)
2439
revids = set(response_handler.read_body_bytes().splitlines())
2440
for fallback in self._fallback_repositories:
2441
revids.update(set(fallback.all_revision_ids()))
2444
def _filtered_revision_trees(self, revision_ids, file_ids):
2445
"""Return Tree for a revision on this branch with only some files.
2447
:param revision_ids: a sequence of revision-ids;
2448
a revision-id may not be None or 'null:'
2449
:param file_ids: if not None, the result is filtered
2450
so that only those file-ids, their parents and their
2451
children are included.
2453
inventories = self.iter_inventories(revision_ids)
2454
for inv in inventories:
2455
# Should we introduce a FilteredRevisionTree class rather
2456
# than pre-filter the inventory here?
2457
filtered_inv = inv.filter(file_ids)
2458
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
2461
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2462
medium = self._client._medium
2463
if medium._is_remote_before((1, 2)):
2465
for delta in self._real_repository.get_deltas_for_revisions(
2466
revisions, specific_fileids):
2469
# Get the revision-ids of interest
2470
required_trees = set()
2471
for revision in revisions:
2472
required_trees.add(revision.revision_id)
2473
required_trees.update(revision.parent_ids[:1])
2475
# Get the matching filtered trees. Note that it's more
2476
# efficient to pass filtered trees to changes_from() rather
2477
# than doing the filtering afterwards. changes_from() could
2478
# arguably do the filtering itself but it's path-based, not
2479
# file-id based, so filtering before or afterwards is
2481
if specific_fileids is None:
2482
trees = dict((t.get_revision_id(), t) for
2483
t in self.revision_trees(required_trees))
2485
trees = dict((t.get_revision_id(), t) for
2486
t in self._filtered_revision_trees(required_trees,
2489
# Calculate the deltas
2490
for revision in revisions:
2491
if not revision.parent_ids:
2492
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2494
old_tree = trees[revision.parent_ids[0]]
2495
yield trees[revision.revision_id].changes_from(old_tree)
2498
def get_revision_delta(self, revision_id, specific_fileids=None):
2499
r = self.get_revision(revision_id)
2500
return list(self.get_deltas_for_revisions([r],
2501
specific_fileids=specific_fileids))[0]
1014
return self._real_repository.all_revision_ids()
1017
def get_deltas_for_revisions(self, revisions):
1019
return self._real_repository.get_deltas_for_revisions(revisions)
1022
def get_revision_delta(self, revision_id):
1024
return self._real_repository.get_revision_delta(revision_id)
2503
1026
@needs_read_lock
2504
1027
def revision_trees(self, revision_ids):
2505
inventories = self.iter_inventories(revision_ids)
2506
for inv in inventories:
2507
yield InventoryRevisionTree(self, inv, inv.revision_id)
1029
return self._real_repository.revision_trees(revision_ids)
2509
1031
@needs_read_lock
2510
1032
def get_revision_reconcile(self, revision_id):
2635
1131
self._ensure_real()
2636
1132
return self._real_repository.texts
2638
def _iter_revisions_rpc(self, revision_ids):
2639
body = "\n".join(revision_ids)
2640
path = self.bzrdir._path_for_remote_call(self._client)
2641
response_tuple, response_handler = (
2642
self._call_with_body_bytes_expecting_body(
2643
"Repository.iter_revisions", (path, ), body))
2644
if response_tuple[0] != "ok":
2645
raise errors.UnexpectedSmartServerResponse(response_tuple)
2646
serializer_format = response_tuple[1]
2647
serializer = serializer_format_registry.get(serializer_format)
2648
byte_stream = response_handler.read_streamed_body()
2649
decompressor = zlib.decompressobj()
2651
for bytes in byte_stream:
2652
chunks.append(decompressor.decompress(bytes))
2653
if decompressor.unused_data != "":
2654
chunks.append(decompressor.flush())
2655
yield serializer.read_revision_from_string("".join(chunks))
2656
unused = decompressor.unused_data
2657
decompressor = zlib.decompressobj()
2658
chunks = [decompressor.decompress(unused)]
2659
chunks.append(decompressor.flush())
2660
text = "".join(chunks)
2662
yield serializer.read_revision_from_string("".join(chunks))
2664
1134
@needs_read_lock
2665
1135
def get_revisions(self, revision_ids):
2666
if revision_ids is None:
2667
revision_ids = self.all_revision_ids()
2669
for rev_id in revision_ids:
2670
if not rev_id or not isinstance(rev_id, basestring):
2671
raise errors.InvalidRevisionId(
2672
revision_id=rev_id, branch=self)
2674
missing = set(revision_ids)
2676
for rev in self._iter_revisions_rpc(revision_ids):
2677
missing.remove(rev.revision_id)
2678
revs[rev.revision_id] = rev
2679
except errors.UnknownSmartMethod:
2681
return self._real_repository.get_revisions(revision_ids)
2682
for fallback in self._fallback_repositories:
2685
for revid in list(missing):
2686
# XXX JRV 2011-11-20: It would be nice if there was a
2687
# public method on Repository that could be used to query
2688
# for revision objects *without* failing completely if one
2689
# was missing. There is VersionedFileRepository._iter_revisions,
2690
# but unfortunately that's private and not provided by
2691
# all repository implementations.
2693
revs[revid] = fallback.get_revision(revid)
2694
except errors.NoSuchRevision:
2697
missing.remove(revid)
2699
raise errors.NoSuchRevision(self, list(missing)[0])
2700
return [revs[revid] for revid in revision_ids]
1137
return self._real_repository.get_revisions(revision_ids)
2702
1139
def supports_rich_root(self):
2703
return self._format.rich_root_data
1141
return self._real_repository.supports_rich_root()
1143
def iter_reverse_revision_history(self, revision_id):
1145
return self._real_repository.iter_reverse_revision_history(revision_id)
2706
1148
def _serializer(self):
2707
return self._format._serializer
1150
return self._real_repository._serializer
2710
1152
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2711
signature = gpg_strategy.sign(plaintext)
2712
self.add_signature_text(revision_id, signature)
1154
return self._real_repository.store_revision_signature(
1155
gpg_strategy, plaintext, revision_id)
2714
1157
def add_signature_text(self, revision_id, signature):
2715
if self._real_repository:
2716
# If there is a real repository the write group will
2717
# be in the real repository as well, so use that:
2719
return self._real_repository.add_signature_text(
2720
revision_id, signature)
2721
path = self.bzrdir._path_for_remote_call(self._client)
2722
response, handler = self._call_with_body_bytes_expecting_body(
2723
'Repository.add_signature_text', (path, self._lock_token,
2724
revision_id) + tuple(self._write_group_tokens), signature)
2725
handler.cancel_read_body()
2727
if response[0] != 'ok':
2728
raise errors.UnexpectedSmartServerResponse(response)
2729
self._write_group_tokens = response[1:]
1159
return self._real_repository.add_signature_text(revision_id, signature)
2731
1161
def has_signature_for_revision_id(self, revision_id):
2732
path = self.bzrdir._path_for_remote_call(self._client)
2734
response = self._call('Repository.has_signature_for_revision_id',
2736
except errors.UnknownSmartMethod:
2738
return self._real_repository.has_signature_for_revision_id(
2740
if response[0] not in ('yes', 'no'):
2741
raise SmartProtocolError('unexpected response code %s' % (response,))
2742
if response[0] == 'yes':
2744
for fallback in self._fallback_repositories:
2745
if fallback.has_signature_for_revision_id(revision_id):
2750
def verify_revision_signature(self, revision_id, gpg_strategy):
2751
if not self.has_signature_for_revision_id(revision_id):
2752
return gpg.SIGNATURE_NOT_SIGNED, None
2753
signature = self.get_signature_text(revision_id)
2755
testament = _mod_testament.Testament.from_revision(self, revision_id)
2756
plaintext = testament.as_short_text()
2758
return gpg_strategy.verify(signature, plaintext)
1163
return self._real_repository.has_signature_for_revision_id(revision_id)
2760
1165
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2761
1166
self._ensure_real()
2762
1167
return self._real_repository.item_keys_introduced_by(revision_ids,
2763
1168
_files_pb=_files_pb)
2765
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2767
return self._real_repository._find_inconsistent_revision_parents(
1170
def revision_graph_can_have_wrong_parents(self):
1171
# The answer depends on the remote repo format.
1173
return self._real_repository.revision_graph_can_have_wrong_parents()
1175
def _find_inconsistent_revision_parents(self):
1177
return self._real_repository._find_inconsistent_revision_parents()
2770
1179
def _check_for_inconsistent_revision_parents(self):
2771
1180
self._ensure_real()
2801
1207
self._ensure_real()
2802
1208
self._real_repository._pack_collection.autopack()
1210
if self._real_repository is not None:
1211
# Reset the real repository's cache of pack names.
1212
# XXX: At some point we may be able to skip this and just rely on
1213
# the automatic retry logic to do the right thing, but for now we
1214
# err on the side of being correct rather than being optimal.
1215
self._real_repository._pack_collection.reload_pack_names()
2805
1216
if response[0] != 'ok':
2806
1217
raise errors.UnexpectedSmartServerResponse(response)
2809
class RemoteStreamSink(vf_repository.StreamSink):
2811
def _insert_real(self, stream, src_format, resume_tokens):
2812
self.target_repo._ensure_real()
2813
sink = self.target_repo._real_repository._get_sink()
2814
result = sink.insert_stream(stream, src_format, resume_tokens)
2816
self.target_repo.autopack()
2819
def insert_stream(self, stream, src_format, resume_tokens):
2820
target = self.target_repo
2821
target._unstacked_provider.missing_keys.clear()
2822
candidate_calls = [('Repository.insert_stream_1.19', (1, 19))]
2823
if target._lock_token:
2824
candidate_calls.append(('Repository.insert_stream_locked', (1, 14)))
2825
lock_args = (target._lock_token or '',)
2827
candidate_calls.append(('Repository.insert_stream', (1, 13)))
2829
client = target._client
2830
medium = client._medium
2831
path = target.bzrdir._path_for_remote_call(client)
2832
# Probe for the verb to use with an empty stream before sending the
2833
# real stream to it. We do this both to avoid the risk of sending a
2834
# large request that is then rejected, and because we don't want to
2835
# implement a way to buffer, rewind, or restart the stream.
2837
for verb, required_version in candidate_calls:
2838
if medium._is_remote_before(required_version):
2841
# We've already done the probing (and set _is_remote_before) on
2842
# a previous insert.
2845
byte_stream = smart_repo._stream_to_byte_stream([], src_format)
2847
response = client.call_with_body_stream(
2848
(verb, path, '') + lock_args, byte_stream)
2849
except errors.UnknownSmartMethod:
2850
medium._remember_remote_is_before(required_version)
2856
return self._insert_real(stream, src_format, resume_tokens)
2857
self._last_inv_record = None
2858
self._last_substream = None
2859
if required_version < (1, 19):
2860
# Remote side doesn't support inventory deltas. Wrap the stream to
2861
# make sure we don't send any. If the stream contains inventory
2862
# deltas we'll interrupt the smart insert_stream request and
2864
stream = self._stop_stream_if_inventory_delta(stream)
2865
byte_stream = smart_repo._stream_to_byte_stream(
2867
resume_tokens = ' '.join(resume_tokens)
2868
response = client.call_with_body_stream(
2869
(verb, path, resume_tokens) + lock_args, byte_stream)
2870
if response[0][0] not in ('ok', 'missing-basis'):
2871
raise errors.UnexpectedSmartServerResponse(response)
2872
if self._last_substream is not None:
2873
# The stream included an inventory-delta record, but the remote
2874
# side isn't new enough to support them. So we need to send the
2875
# rest of the stream via VFS.
2876
self.target_repo.refresh_data()
2877
return self._resume_stream_with_vfs(response, src_format)
2878
if response[0][0] == 'missing-basis':
2879
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
2880
resume_tokens = tokens
2881
return resume_tokens, set(missing_keys)
2883
self.target_repo.refresh_data()
2886
def _resume_stream_with_vfs(self, response, src_format):
2887
"""Resume sending a stream via VFS, first resending the record and
2888
substream that couldn't be sent via an insert_stream verb.
2890
if response[0][0] == 'missing-basis':
2891
tokens, missing_keys = bencode.bdecode_as_tuple(response[0][1])
2892
# Ignore missing_keys, we haven't finished inserting yet
2895
def resume_substream():
2896
# Yield the substream that was interrupted.
2897
for record in self._last_substream:
2899
self._last_substream = None
2900
def resume_stream():
2901
# Finish sending the interrupted substream
2902
yield ('inventory-deltas', resume_substream())
2903
# Then simply continue sending the rest of the stream.
2904
for substream_kind, substream in self._last_stream:
2905
yield substream_kind, substream
2906
return self._insert_real(resume_stream(), src_format, tokens)
2908
def _stop_stream_if_inventory_delta(self, stream):
2909
"""Normally this just lets the original stream pass-through unchanged.
2911
However if any 'inventory-deltas' substream occurs it will stop
2912
streaming, and store the interrupted substream and stream in
2913
self._last_substream and self._last_stream so that the stream can be
2914
resumed by _resume_stream_with_vfs.
2917
stream_iter = iter(stream)
2918
for substream_kind, substream in stream_iter:
2919
if substream_kind == 'inventory-deltas':
2920
self._last_substream = substream
2921
self._last_stream = stream_iter
2924
yield substream_kind, substream
2927
class RemoteStreamSource(vf_repository.StreamSource):
2928
"""Stream data from a remote server."""
2930
def get_stream(self, search):
2931
if (self.from_repository._fallback_repositories and
2932
self.to_format._fetch_order == 'topological'):
2933
return self._real_stream(self.from_repository, search)
2936
repos = [self.from_repository]
2942
repos.extend(repo._fallback_repositories)
2943
sources.append(repo)
2944
return self.missing_parents_chain(search, sources)
2946
def get_stream_for_missing_keys(self, missing_keys):
2947
self.from_repository._ensure_real()
2948
real_repo = self.from_repository._real_repository
2949
real_source = real_repo._get_source(self.to_format)
2950
return real_source.get_stream_for_missing_keys(missing_keys)
2952
def _real_stream(self, repo, search):
2953
"""Get a stream for search from repo.
2955
This never called RemoteStreamSource.get_stream, and is a helper
2956
for RemoteStreamSource._get_stream to allow getting a stream
2957
reliably whether fallback back because of old servers or trying
2958
to stream from a non-RemoteRepository (which the stacked support
2961
source = repo._get_source(self.to_format)
2962
if isinstance(source, RemoteStreamSource):
2964
source = repo._real_repository._get_source(self.to_format)
2965
return source.get_stream(search)
2967
def _get_stream(self, repo, search):
2968
"""Core worker to get a stream from repo for search.
2970
This is used by both get_stream and the stacking support logic. It
2971
deliberately gets a stream for repo which does not need to be
2972
self.from_repository. In the event that repo is not Remote, or
2973
cannot do a smart stream, a fallback is made to the generic
2974
repository._get_stream() interface, via self._real_stream.
2976
In the event of stacking, streams from _get_stream will not
2977
contain all the data for search - this is normal (see get_stream).
2979
:param repo: A repository.
2980
:param search: A search.
2982
# Fallbacks may be non-smart
2983
if not isinstance(repo, RemoteRepository):
2984
return self._real_stream(repo, search)
2985
client = repo._client
2986
medium = client._medium
2987
path = repo.bzrdir._path_for_remote_call(client)
2988
search_bytes = repo._serialise_search_result(search)
2989
args = (path, self.to_format.network_name())
2991
('Repository.get_stream_1.19', (1, 19)),
2992
('Repository.get_stream', (1, 13))]
2995
for verb, version in candidate_verbs:
2996
if medium._is_remote_before(version):
2999
response = repo._call_with_body_bytes_expecting_body(
3000
verb, args, search_bytes)
3001
except errors.UnknownSmartMethod:
3002
medium._remember_remote_is_before(version)
3003
except errors.UnknownErrorFromSmartServer, e:
3004
if isinstance(search, vf_search.EverythingResult):
3005
error_verb = e.error_from_smart_server.error_verb
3006
if error_verb == 'BadSearch':
3007
# Pre-2.4 servers don't support this sort of search.
3008
# XXX: perhaps falling back to VFS on BadSearch is a
3009
# good idea in general? It might provide a little bit
3010
# of protection against client-side bugs.
3011
medium._remember_remote_is_before((2, 4))
3015
response_tuple, response_handler = response
3019
return self._real_stream(repo, search)
3020
if response_tuple[0] != 'ok':
3021
raise errors.UnexpectedSmartServerResponse(response_tuple)
3022
byte_stream = response_handler.read_streamed_body()
3023
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
3024
self._record_counter)
3025
if src_format.network_name() != repo._format.network_name():
3026
raise AssertionError(
3027
"Mismatched RemoteRepository and stream src %r, %r" % (
3028
src_format.network_name(), repo._format.network_name()))
3031
def missing_parents_chain(self, search, sources):
3032
"""Chain multiple streams together to handle stacking.
3034
:param search: The overall search to satisfy with streams.
3035
:param sources: A list of Repository objects to query.
3037
self.from_serialiser = self.from_repository._format._serializer
3038
self.seen_revs = set()
3039
self.referenced_revs = set()
3040
# If there are heads in the search, or the key count is > 0, we are not
3042
while not search.is_empty() and len(sources) > 1:
3043
source = sources.pop(0)
3044
stream = self._get_stream(source, search)
3045
for kind, substream in stream:
3046
if kind != 'revisions':
3047
yield kind, substream
3049
yield kind, self.missing_parents_rev_handler(substream)
3050
search = search.refine(self.seen_revs, self.referenced_revs)
3051
self.seen_revs = set()
3052
self.referenced_revs = set()
3053
if not search.is_empty():
3054
for kind, stream in self._get_stream(sources[0], search):
3057
def missing_parents_rev_handler(self, substream):
3058
for content in substream:
3059
revision_bytes = content.get_bytes_as('fulltext')
3060
revision = self.from_serialiser.read_revision_from_string(
3062
self.seen_revs.add(content.key[-1])
3063
self.referenced_revs.update(revision.parent_ids)
3067
1220
class RemoteBranchLockableFiles(LockableFiles):
3068
1221
"""A 'LockableFiles' implementation that talks to a smart server.
3070
1223
This is not a public interface class.
3087
1240
class RemoteBranchFormat(branch.BranchFormat):
3089
def __init__(self, network_name=None):
3090
1243
super(RemoteBranchFormat, self).__init__()
3091
1244
self._matchingbzrdir = RemoteBzrDirFormat()
3092
1245
self._matchingbzrdir.set_branch_format(self)
3093
self._custom_format = None
3094
self._network_name = network_name
3096
1247
def __eq__(self, other):
3097
return (isinstance(other, RemoteBranchFormat) and
1248
return (isinstance(other, RemoteBranchFormat) and
3098
1249
self.__dict__ == other.__dict__)
3100
def _ensure_real(self):
3101
if self._custom_format is None:
3103
self._custom_format = branch.network_format_registry.get(
3106
raise errors.UnknownFormatError(kind='branch',
3107
format=self._network_name)
3109
1251
def get_format_description(self):
3111
return 'Remote: ' + self._custom_format.get_format_description()
3113
def network_name(self):
3114
return self._network_name
3116
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
3117
return a_bzrdir.open_branch(name=name,
3118
ignore_fallbacks=ignore_fallbacks)
3120
def _vfs_initialize(self, a_bzrdir, name, append_revisions_only,
3122
# Initialisation when using a local bzrdir object, or a non-vfs init
3123
# method is not available on the server.
3124
# self._custom_format is always set - the start of initialize ensures
3126
if isinstance(a_bzrdir, RemoteBzrDir):
3127
a_bzrdir._ensure_real()
3128
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3129
name=name, append_revisions_only=append_revisions_only,
3130
repository=repository)
3132
# We assume the bzrdir is parameterised; it may not be.
3133
result = self._custom_format.initialize(a_bzrdir, name=name,
3134
append_revisions_only=append_revisions_only,
3135
repository=repository)
3136
if (isinstance(a_bzrdir, RemoteBzrDir) and
3137
not isinstance(result, RemoteBranch)):
3138
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3142
def initialize(self, a_bzrdir, name=None, repository=None,
3143
append_revisions_only=None):
3145
name = a_bzrdir._get_selected_branch()
3146
# 1) get the network name to use.
3147
if self._custom_format:
3148
network_name = self._custom_format.network_name()
3150
# Select the current bzrlib default and ask for that.
3151
reference_bzrdir_format = controldir.format_registry.get('default')()
3152
reference_format = reference_bzrdir_format.get_branch_format()
3153
self._custom_format = reference_format
3154
network_name = reference_format.network_name()
3155
# Being asked to create on a non RemoteBzrDir:
3156
if not isinstance(a_bzrdir, RemoteBzrDir):
3157
return self._vfs_initialize(a_bzrdir, name=name,
3158
append_revisions_only=append_revisions_only,
3159
repository=repository)
3160
medium = a_bzrdir._client._medium
3161
if medium._is_remote_before((1, 13)):
3162
return self._vfs_initialize(a_bzrdir, name=name,
3163
append_revisions_only=append_revisions_only,
3164
repository=repository)
3165
# Creating on a remote bzr dir.
3166
# 2) try direct creation via RPC
3167
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
3169
# XXX JRV20100304: Support creating colocated branches
3170
raise errors.NoColocatedBranchSupport(self)
3171
verb = 'BzrDir.create_branch'
3173
response = a_bzrdir._call(verb, path, network_name)
3174
except errors.UnknownSmartMethod:
3175
# Fallback - use vfs methods
3176
medium._remember_remote_is_before((1, 13))
3177
return self._vfs_initialize(a_bzrdir, name=name,
3178
append_revisions_only=append_revisions_only,
3179
repository=repository)
3180
if response[0] != 'ok':
3181
raise errors.UnexpectedSmartServerResponse(response)
3182
# Turn the response into a RemoteRepository object.
3183
format = RemoteBranchFormat(network_name=response[1])
3184
repo_format = response_tuple_to_repo_format(response[3:])
3185
repo_path = response[2]
3186
if repository is not None:
3187
remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
3188
url_diff = urlutils.relative_url(repository.user_url,
3191
raise AssertionError(
3192
'repository.user_url %r does not match URL from server '
3193
'response (%r + %r)'
3194
% (repository.user_url, a_bzrdir.user_url, repo_path))
3195
remote_repo = repository
3198
repo_bzrdir = a_bzrdir
3200
repo_bzrdir = RemoteBzrDir(
3201
a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
3203
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3204
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
3205
format=format, setup_stacking=False, name=name)
3206
if append_revisions_only:
3207
remote_branch.set_append_revisions_only(append_revisions_only)
3208
# XXX: We know this is a new branch, so it must have revno 0, revid
3209
# NULL_REVISION. Creating the branch locked would make this be unable
3210
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3211
remote_branch._last_revision_info_cache = 0, NULL_REVISION
3212
return remote_branch
3214
def make_tags(self, branch):
3216
return self._custom_format.make_tags(branch)
1252
return 'Remote BZR Branch'
1254
def get_format_string(self):
1255
return 'Remote BZR Branch'
1257
def open(self, a_bzrdir):
1258
return a_bzrdir.open_branch()
1260
def initialize(self, a_bzrdir):
1261
return a_bzrdir.create_branch()
3218
1263
def supports_tags(self):
3219
1264
# Remote branches might support tags, but we won't know until we
3220
1265
# access the real remote branch.
3222
return self._custom_format.supports_tags()
3224
def supports_stacking(self):
3226
return self._custom_format.supports_stacking()
3228
def supports_set_append_revisions_only(self):
3230
return self._custom_format.supports_set_append_revisions_only()
3232
def _use_default_local_heads_to_fetch(self):
3233
# If the branch format is a metadir format *and* its heads_to_fetch
3234
# implementation is not overridden vs the base class, we can use the
3235
# base class logic rather than use the heads_to_fetch RPC. This is
3236
# usually cheaper in terms of net round trips, as the last-revision and
3237
# tags info fetched is cached and would be fetched anyway.
3239
if isinstance(self._custom_format, branch.BranchFormatMetadir):
3240
branch_class = self._custom_format._branch_class()
3241
heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
3242
if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
3247
class RemoteBranchStore(_mod_config.IniFileStore):
3248
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3250
Note that this is specific to bzr-based formats.
3253
def __init__(self, branch):
3254
super(RemoteBranchStore, self).__init__()
3255
self.branch = branch
3257
self._real_store = None
3259
def external_url(self):
3260
return urlutils.join(self.branch.user_url, 'branch.conf')
3262
def _load_content(self):
3263
path = self.branch._remote_path()
3265
response, handler = self.branch._call_expecting_body(
3266
'Branch.get_config_file', path)
3267
except errors.UnknownSmartMethod:
3269
return self._real_store._load_content()
3270
if len(response) and response[0] != 'ok':
3271
raise errors.UnexpectedSmartServerResponse(response)
3272
return handler.read_body_bytes()
3274
def _save_content(self, content):
3275
path = self.branch._remote_path()
3277
response, handler = self.branch._call_with_body_bytes_expecting_body(
3278
'Branch.put_config_file', (path,
3279
self.branch._lock_token, self.branch._repo_lock_token),
3281
except errors.UnknownSmartMethod:
3283
return self._real_store._save_content(content)
3284
handler.cancel_read_body()
3285
if response != ('ok', ):
3286
raise errors.UnexpectedSmartServerResponse(response)
3288
def _ensure_real(self):
3289
self.branch._ensure_real()
3290
if self._real_store is None:
3291
self._real_store = _mod_config.BranchStore(self.branch)
3294
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
1269
class RemoteBranch(branch.Branch, _RpcHelper):
3295
1270
"""Branch stored on a server accessed by HPSS RPC.
3297
1272
At the moment most operations are mapped down to simple file operations.
3300
1275
def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
3301
_client=None, format=None, setup_stacking=True, name=None,
3302
possible_transports=None):
3303
1277
"""Create a RemoteBranch instance.
3305
1279
:param real_branch: An optional local implementation of the branch
3306
1280
format, usually accessing the data via the VFS.
3307
1281
:param _client: Private parameter for testing.
3308
:param format: A RemoteBranchFormat object, None to create one
3309
automatically. If supplied it should have a network_name already
3311
:param setup_stacking: If True make an RPC call to determine the
3312
stacked (or not) status of the branch. If False assume the branch
3314
:param name: Colocated branch name
3316
1283
# We intentionally don't call the parent class's __init__, because it
3317
1284
# will try to assign to self.tags, which is a property in this subclass.
3318
1285
# And the parent's __init__ doesn't do much anyway.
1286
self._revision_id_to_revno_cache = None
1287
self._revision_history_cache = None
1288
self._last_revision_info_cache = None
3319
1289
self.bzrdir = remote_bzrdir
3321
1290
if _client is not None:
3322
1291
self._client = _client
3516
1438
raise errors.UnexpectedSmartServerResponse(response)
3517
1439
return response[1]
3519
def set_stacked_on_url(self, url):
3520
branch.Branch.set_stacked_on_url(self, url)
3521
# We need the stacked_on_url to be visible both locally (to not query
3522
# it repeatedly) and remotely (so smart verbs can get it server side)
3523
# Without the following line,
3524
# bzrlib.tests.per_branch.test_create_clone.TestCreateClone
3525
# .test_create_clone_on_transport_stacked_hooks_get_stacked_branch
3526
# fails for remote branches -- vila 2012-01-04
3527
self.conf_store.save_changes()
3529
self._is_stacked = False
3531
self._is_stacked = True
3533
def _vfs_get_tags_bytes(self):
3535
return self._real_branch._get_tags_bytes()
3538
def _get_tags_bytes(self):
3539
if self._tags_bytes is None:
3540
self._tags_bytes = self._get_tags_bytes_via_hpss()
3541
return self._tags_bytes
3543
def _get_tags_bytes_via_hpss(self):
3544
medium = self._client._medium
3545
if medium._is_remote_before((1, 13)):
3546
return self._vfs_get_tags_bytes()
3548
response = self._call('Branch.get_tags_bytes', self._remote_path())
3549
except errors.UnknownSmartMethod:
3550
medium._remember_remote_is_before((1, 13))
3551
return self._vfs_get_tags_bytes()
3554
def _vfs_set_tags_bytes(self, bytes):
3556
return self._real_branch._set_tags_bytes(bytes)
3558
def _set_tags_bytes(self, bytes):
3559
if self.is_locked():
3560
self._tags_bytes = bytes
3561
medium = self._client._medium
3562
if medium._is_remote_before((1, 18)):
3563
self._vfs_set_tags_bytes(bytes)
3567
self._remote_path(), self._lock_token, self._repo_lock_token)
3568
response = self._call_with_body_bytes(
3569
'Branch.set_tags_bytes', args, bytes)
3570
except errors.UnknownSmartMethod:
3571
medium._remember_remote_is_before((1, 18))
3572
self._vfs_set_tags_bytes(bytes)
3574
1441
def lock_read(self):
3575
"""Lock the branch for read operations.
3577
:return: A bzrlib.lock.LogicalLockResult.
3579
1442
self.repository.lock_read()
3580
1443
if not self._lock_mode:
3581
self._note_lock('r')
3582
1444
self._lock_mode = 'r'
3583
1445
self._lock_count = 1
3584
1446
if self._real_branch is not None:
3585
1447
self._real_branch.lock_read()
3587
1449
self._lock_count += 1
3588
return lock.LogicalLockResult(self.unlock)
3590
1451
def _remote_lock_write(self, token):
3591
1452
if token is None:
3592
1453
branch_token = repo_token = ''
3594
1455
branch_token = token
3595
repo_token = self.repository.lock_write().repository_token
1456
repo_token = self.repository.lock_write()
3596
1457
self.repository.unlock()
3597
1458
err_context = {'token': token}
3599
response = self._call(
3600
'Branch.lock_write', self._remote_path(), branch_token,
3601
repo_token or '', **err_context)
3602
except errors.LockContention, e:
3603
# The LockContention from the server doesn't have any
3604
# information about the lock_url. We re-raise LockContention
3605
# with valid lock_url.
3606
raise errors.LockContention('(remote lock)',
3607
self.repository.base.split('.bzr/')[0])
1459
response = self._call(
1460
'Branch.lock_write', self._remote_path(), branch_token,
1461
repo_token or '', **err_context)
3608
1462
if response[0] != 'ok':
3609
1463
raise errors.UnexpectedSmartServerResponse(response)
3610
1464
ok, branch_token, repo_token = response
3611
1465
return branch_token, repo_token
3613
1467
def lock_write(self, token=None):
3614
1468
if not self._lock_mode:
3615
self._note_lock('w')
3616
1469
# Lock the branch and repo in one remote call.
3617
1470
remote_tokens = self._remote_lock_write(token)
3618
1471
self._lock_token, self._repo_lock_token = remote_tokens
3771
1585
raise errors.UnexpectedSmartServerResponse(response)
3772
1586
new_revno, new_revision_id = response[1:]
3773
1587
self._last_revision_info_cache = new_revno, new_revision_id
3774
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
3775
1588
if self._real_branch is not None:
3776
1589
cache = new_revno, new_revision_id
3777
1590
self._real_branch._last_revision_info_cache = cache
3779
1592
def _set_last_revision(self, revision_id):
3780
old_revno, old_revid = self.last_revision_info()
3781
# This performs additional work to meet the hook contract; while its
3782
# undesirable, we have to synthesise the revno to call the hook, and
3783
# not calling the hook is worse as it means changes can't be prevented.
3784
# Having calculated this though, we can't just call into
3785
# set_last_revision_info as a simple call, because there is a set_rh
3786
# hook that some folk may still be using.
3787
history = self._lefthand_history(revision_id)
3788
self._run_pre_change_branch_tip_hooks(len(history), revision_id)
3789
1593
self._clear_cached_state()
3790
1594
response = self._call('Branch.set_last_revision',
3791
1595
self._remote_path(), self._lock_token, self._repo_lock_token,
3793
1597
if response != ('ok',):
3794
1598
raise errors.UnexpectedSmartServerResponse(response)
3795
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
3797
def _get_parent_location(self):
3798
medium = self._client._medium
3799
if medium._is_remote_before((1, 13)):
3800
return self._vfs_get_parent_location()
3802
response = self._call('Branch.get_parent', self._remote_path())
3803
except errors.UnknownSmartMethod:
3804
medium._remember_remote_is_before((1, 13))
3805
return self._vfs_get_parent_location()
3806
if len(response) != 1:
3807
raise errors.UnexpectedSmartServerResponse(response)
3808
parent_location = response[0]
3809
if parent_location == '':
3811
return parent_location
3813
def _vfs_get_parent_location(self):
3815
return self._real_branch._get_parent_location()
3817
def _set_parent_location(self, url):
3818
medium = self._client._medium
3819
if medium._is_remote_before((1, 15)):
3820
return self._vfs_set_parent_location(url)
3822
call_url = url or ''
3823
if type(call_url) is not str:
3824
raise AssertionError('url must be a str or None (%s)' % url)
3825
response = self._call('Branch.set_parent_location',
3826
self._remote_path(), self._lock_token, self._repo_lock_token,
3828
except errors.UnknownSmartMethod:
3829
medium._remember_remote_is_before((1, 15))
3830
return self._vfs_set_parent_location(url)
3832
raise errors.UnexpectedSmartServerResponse(response)
3834
def _vfs_set_parent_location(self, url):
3836
return self._real_branch._set_parent_location(url)
1601
def set_revision_history(self, rev_history):
1602
# Send just the tip revision of the history; the server will generate
1603
# the full history from that. If the revision doesn't exist in this
1604
# branch, NoSuchRevision will be raised.
1605
if rev_history == []:
1608
rev_id = rev_history[-1]
1609
self._set_last_revision(rev_id)
1610
self._cache_revision_history(rev_history)
1612
def get_parent(self):
1614
return self._real_branch.get_parent()
1616
def set_parent(self, url):
1618
return self._real_branch.set_parent(url)
1620
def set_stacked_on_url(self, stacked_location):
1621
"""Set the URL this branch is stacked against.
1623
:raises UnstackableBranchFormat: If the branch does not support
1625
:raises UnstackableRepositoryFormat: If the repository does not support
1629
return self._real_branch.set_stacked_on_url(stacked_location)
1631
def sprout(self, to_bzrdir, revision_id=None):
1632
branch_format = to_bzrdir._format._branch_format
1633
if (branch_format is None or
1634
isinstance(branch_format, RemoteBranchFormat)):
1635
# The to_bzrdir specifies RemoteBranchFormat (or no format, which
1636
# implies the same thing), but RemoteBranches can't be created at
1637
# arbitrary URLs. So create a branch in the same format as
1638
# _real_branch instead.
1639
# XXX: if to_bzrdir is a RemoteBzrDir, this should perhaps do
1640
# to_bzrdir.create_branch to create a RemoteBranch after all...
1642
result = self._real_branch._format.initialize(to_bzrdir)
1643
self.copy_content_into(result, revision_id=revision_id)
1644
result.set_parent(self.bzrdir.root_transport.base)
1646
result = branch.Branch.sprout(
1647
self, to_bzrdir, revision_id=revision_id)
3838
1650
@needs_write_lock
3839
1651
def pull(self, source, overwrite=False, stop_revision=None,
3934
1706
except errors.UnknownSmartMethod:
3935
1707
medium._remember_remote_is_before((1, 6))
3936
1708
self._clear_cached_state_of_remote_branch_only()
3937
graph = self.repository.get_graph()
3938
(last_revno, last_revid) = self.last_revision_info()
3939
known_revision_ids = [
3940
(last_revid, last_revno),
3941
(_mod_revision.NULL_REVISION, 0),
3943
if last_rev is not None:
3944
if not graph.is_ancestor(last_rev, revision_id):
3945
# our previous tip is not merged into stop_revision
3946
raise errors.DivergedBranches(self, other_branch)
3947
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
3948
self.set_last_revision_info(revno, revision_id)
1710
self._real_branch.generate_revision_history(
1711
revision_id, last_rev=last_rev, other_branch=other_branch)
1716
return self._real_branch.tags
3950
1718
def set_push_location(self, location):
3951
self._set_config_location('push_location', location)
3953
def heads_to_fetch(self):
3954
if self._format._use_default_local_heads_to_fetch():
3955
# We recognise this format, and its heads-to-fetch implementation
3956
# is the default one (tip + tags). In this case it's cheaper to
3957
# just use the default implementation rather than a special RPC as
3958
# the tip and tags data is cached.
3959
return branch.Branch.heads_to_fetch(self)
3960
medium = self._client._medium
3961
if medium._is_remote_before((2, 4)):
3962
return self._vfs_heads_to_fetch()
3964
return self._rpc_heads_to_fetch()
3965
except errors.UnknownSmartMethod:
3966
medium._remember_remote_is_before((2, 4))
3967
return self._vfs_heads_to_fetch()
3969
def _rpc_heads_to_fetch(self):
3970
response = self._call('Branch.heads_to_fetch', self._remote_path())
3971
if len(response) != 2:
3972
raise errors.UnexpectedSmartServerResponse(response)
3973
must_fetch, if_present_fetch = response
3974
return set(must_fetch), set(if_present_fetch)
3976
def _vfs_heads_to_fetch(self):
3977
1719
self._ensure_real()
3978
return self._real_branch.heads_to_fetch()
3981
class RemoteConfig(object):
3982
"""A Config that reads and writes from smart verbs.
3984
It is a low-level object that considers config data to be name/value pairs
3985
that may be associated with a section. Assigning meaning to the these
3986
values is done at higher levels like bzrlib.config.TreeConfig.
3989
def get_option(self, name, section=None, default=None):
3990
"""Return the value associated with a named option.
3992
:param name: The name of the value
3993
:param section: The section the option is in (if any)
3994
:param default: The value to return if the value is not set
3995
:return: The value or default value
3998
configobj = self._get_configobj()
4001
section_obj = configobj
4004
section_obj = configobj[section]
4007
if section_obj is None:
4010
value = section_obj.get(name, default)
4011
except errors.UnknownSmartMethod:
4012
value = self._vfs_get_option(name, section, default)
4013
for hook in _mod_config.OldConfigHooks['get']:
4014
hook(self, name, value)
4017
def _response_to_configobj(self, response):
4018
if len(response[0]) and response[0][0] != 'ok':
4019
raise errors.UnexpectedSmartServerResponse(response)
4020
lines = response[1].read_body_bytes().splitlines()
4021
conf = _mod_config.ConfigObj(lines, encoding='utf-8')
4022
for hook in _mod_config.OldConfigHooks['load']:
4027
class RemoteBranchConfig(RemoteConfig):
4028
"""A RemoteConfig for Branches."""
4030
def __init__(self, branch):
4031
self._branch = branch
4033
def _get_configobj(self):
4034
path = self._branch._remote_path()
4035
response = self._branch._client.call_expecting_body(
4036
'Branch.get_config_file', path)
4037
return self._response_to_configobj(response)
4039
def set_option(self, value, name, section=None):
4040
"""Set the value associated with a named option.
4042
:param value: The value to set
4043
:param name: The name of the value to set
4044
:param section: The section the option is in (if any)
4046
medium = self._branch._client._medium
4047
if medium._is_remote_before((1, 14)):
4048
return self._vfs_set_option(value, name, section)
4049
if isinstance(value, dict):
4050
if medium._is_remote_before((2, 2)):
4051
return self._vfs_set_option(value, name, section)
4052
return self._set_config_option_dict(value, name, section)
4054
return self._set_config_option(value, name, section)
4056
def _set_config_option(self, value, name, section):
4058
path = self._branch._remote_path()
4059
response = self._branch._client.call('Branch.set_config_option',
4060
path, self._branch._lock_token, self._branch._repo_lock_token,
4061
value.encode('utf8'), name, section or '')
4062
except errors.UnknownSmartMethod:
4063
medium = self._branch._client._medium
4064
medium._remember_remote_is_before((1, 14))
4065
return self._vfs_set_option(value, name, section)
4067
raise errors.UnexpectedSmartServerResponse(response)
4069
def _serialize_option_dict(self, option_dict):
4071
for key, value in option_dict.items():
4072
if isinstance(key, unicode):
4073
key = key.encode('utf8')
4074
if isinstance(value, unicode):
4075
value = value.encode('utf8')
4076
utf8_dict[key] = value
4077
return bencode.bencode(utf8_dict)
4079
def _set_config_option_dict(self, value, name, section):
4081
path = self._branch._remote_path()
4082
serialised_dict = self._serialize_option_dict(value)
4083
response = self._branch._client.call(
4084
'Branch.set_config_option_dict',
4085
path, self._branch._lock_token, self._branch._repo_lock_token,
4086
serialised_dict, name, section or '')
4087
except errors.UnknownSmartMethod:
4088
medium = self._branch._client._medium
4089
medium._remember_remote_is_before((2, 2))
4090
return self._vfs_set_option(value, name, section)
4092
raise errors.UnexpectedSmartServerResponse(response)
4094
def _real_object(self):
4095
self._branch._ensure_real()
4096
return self._branch._real_branch
4098
def _vfs_set_option(self, value, name, section=None):
4099
return self._real_object()._get_config().set_option(
4100
value, name, section)
4103
class RemoteBzrDirConfig(RemoteConfig):
4104
"""A RemoteConfig for BzrDirs."""
4106
def __init__(self, bzrdir):
4107
self._bzrdir = bzrdir
4109
def _get_configobj(self):
4110
medium = self._bzrdir._client._medium
4111
verb = 'BzrDir.get_config_file'
4112
if medium._is_remote_before((1, 15)):
4113
raise errors.UnknownSmartMethod(verb)
4114
path = self._bzrdir._path_for_remote_call(self._bzrdir._client)
4115
response = self._bzrdir._call_expecting_body(
4117
return self._response_to_configobj(response)
4119
def _vfs_get_option(self, name, section, default):
4120
return self._real_object()._get_config().get_option(
4121
name, section, default)
4123
def set_option(self, value, name, section=None):
4124
"""Set the value associated with a named option.
4126
:param value: The value to set
4127
:param name: The name of the value to set
4128
:param section: The section the option is in (if any)
4130
return self._real_object()._get_config().set_option(
4131
value, name, section)
4133
def _real_object(self):
4134
self._bzrdir._ensure_real()
4135
return self._bzrdir._real_bzrdir
1720
return self._real_branch.set_push_location(location)
1723
def update_revisions(self, other, stop_revision=None, overwrite=False,
1725
"""See Branch.update_revisions."""
1728
if stop_revision is None:
1729
stop_revision = other.last_revision()
1730
if revision.is_null(stop_revision):
1731
# if there are no commits, we're done.
1733
self.fetch(other, stop_revision)
1736
# Just unconditionally set the new revision. We don't care if
1737
# the branches have diverged.
1738
self._set_last_revision(stop_revision)
1740
medium = self._client._medium
1741
if not medium._is_remote_before((1, 6)):
1743
self._set_last_revision_descendant(stop_revision, other)
1745
except errors.UnknownSmartMethod:
1746
medium._remember_remote_is_before((1, 6))
1747
# Fallback for pre-1.6 servers: check for divergence
1748
# client-side, then do _set_last_revision.
1749
last_rev = revision.ensure_null(self.last_revision())
1751
graph = self.repository.get_graph()
1752
if self._check_if_descendant_or_diverged(
1753
stop_revision, last_rev, graph, other):
1754
# stop_revision is a descendant of last_rev, but we aren't
1755
# overwriting, so we're done.
1757
self._set_last_revision(stop_revision)
4138
1762
def _extract_tar(tar, to_dir):
4182
1802
'Missing key %r in context %r', key_err.args[0], context)
4186
translator = error_translators.get(err.error_verb)
4190
raise translator(err, find, get_path)
4192
translator = no_context_error_translators.get(err.error_verb)
4194
raise errors.UnknownErrorFromSmartServer(err)
4196
raise translator(err)
4199
error_translators.register('NoSuchRevision',
4200
lambda err, find, get_path: NoSuchRevision(
4201
find('branch'), err.error_args[0]))
4202
error_translators.register('nosuchrevision',
4203
lambda err, find, get_path: NoSuchRevision(
4204
find('repository'), err.error_args[0]))
4206
def _translate_nobranch_error(err, find, get_path):
4207
if len(err.error_args) >= 1:
4208
extra = err.error_args[0]
4211
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4214
error_translators.register('nobranch', _translate_nobranch_error)
4215
error_translators.register('norepository',
4216
lambda err, find, get_path: errors.NoRepositoryPresent(
4218
error_translators.register('UnlockableTransport',
4219
lambda err, find, get_path: errors.UnlockableTransport(
4220
find('bzrdir').root_transport))
4221
error_translators.register('TokenMismatch',
4222
lambda err, find, get_path: errors.TokenMismatch(
4223
find('token'), '(remote token)'))
4224
error_translators.register('Diverged',
4225
lambda err, find, get_path: errors.DivergedBranches(
4226
find('branch'), find('other_branch')))
4227
error_translators.register('NotStacked',
4228
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4230
def _translate_PermissionDenied(err, find, get_path):
4232
if len(err.error_args) >= 2:
4233
extra = err.error_args[1]
4236
return errors.PermissionDenied(path, extra=extra)
4238
error_translators.register('PermissionDenied', _translate_PermissionDenied)
4239
error_translators.register('ReadError',
4240
lambda err, find, get_path: errors.ReadError(get_path()))
4241
error_translators.register('NoSuchFile',
4242
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4243
error_translators.register('TokenLockingNotSupported',
4244
lambda err, find, get_path: errors.TokenLockingNotSupported(
4245
find('repository')))
4246
error_translators.register('UnsuspendableWriteGroup',
4247
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4248
repository=find('repository')))
4249
error_translators.register('UnresumableWriteGroup',
4250
lambda err, find, get_path: errors.UnresumableWriteGroup(
4251
repository=find('repository'), write_groups=err.error_args[0],
4252
reason=err.error_args[1]))
4253
no_context_error_translators.register('IncompatibleRepositories',
4254
lambda err: errors.IncompatibleRepositories(
4255
err.error_args[0], err.error_args[1], err.error_args[2]))
4256
no_context_error_translators.register('LockContention',
4257
lambda err: errors.LockContention('(remote lock)'))
4258
no_context_error_translators.register('LockFailed',
4259
lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
4260
no_context_error_translators.register('TipChangeRejected',
4261
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4262
no_context_error_translators.register('UnstackableBranchFormat',
4263
lambda err: errors.UnstackableBranchFormat(*err.error_args))
4264
no_context_error_translators.register('UnstackableRepositoryFormat',
4265
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4266
no_context_error_translators.register('FileExists',
4267
lambda err: errors.FileExists(err.error_args[0]))
4268
no_context_error_translators.register('DirectoryNotEmpty',
4269
lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
4271
def _translate_short_readv_error(err):
4272
args = err.error_args
4273
return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
4276
no_context_error_translators.register('ShortReadvError',
4277
_translate_short_readv_error)
4279
def _translate_unicode_error(err):
1805
if err.error_verb == 'NoSuchRevision':
1806
raise NoSuchRevision(find('branch'), err.error_args[0])
1807
elif err.error_verb == 'nosuchrevision':
1808
raise NoSuchRevision(find('repository'), err.error_args[0])
1809
elif err.error_tuple == ('nobranch',):
1810
raise errors.NotBranchError(path=find('bzrdir').root_transport.base)
1811
elif err.error_verb == 'norepository':
1812
raise errors.NoRepositoryPresent(find('bzrdir'))
1813
elif err.error_verb == 'LockContention':
1814
raise errors.LockContention('(remote lock)')
1815
elif err.error_verb == 'UnlockableTransport':
1816
raise errors.UnlockableTransport(find('bzrdir').root_transport)
1817
elif err.error_verb == 'LockFailed':
1818
raise errors.LockFailed(err.error_args[0], err.error_args[1])
1819
elif err.error_verb == 'TokenMismatch':
1820
raise errors.TokenMismatch(find('token'), '(remote token)')
1821
elif err.error_verb == 'Diverged':
1822
raise errors.DivergedBranches(find('branch'), find('other_branch'))
1823
elif err.error_verb == 'TipChangeRejected':
1824
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
1825
elif err.error_verb == 'UnstackableBranchFormat':
1826
raise errors.UnstackableBranchFormat(*err.error_args)
1827
elif err.error_verb == 'UnstackableRepositoryFormat':
1828
raise errors.UnstackableRepositoryFormat(*err.error_args)
1829
elif err.error_verb == 'NotStacked':
1830
raise errors.NotStacked(branch=find('branch'))
1831
elif err.error_verb == 'PermissionDenied':
1833
if len(err.error_args) >= 2:
1834
extra = err.error_args[1]
1837
raise errors.PermissionDenied(path, extra=extra)
1838
elif err.error_verb == 'ReadError':
1840
raise errors.ReadError(path)
1841
elif err.error_verb == 'NoSuchFile':
1843
raise errors.NoSuchFile(path)
1844
elif err.error_verb == 'FileExists':
1845
raise errors.FileExists(err.error_args[0])
1846
elif err.error_verb == 'DirectoryNotEmpty':
1847
raise errors.DirectoryNotEmpty(err.error_args[0])
1848
elif err.error_verb == 'ShortReadvError':
1849
args = err.error_args
1850
raise errors.ShortReadvError(
1851
args[0], int(args[1]), int(args[2]), int(args[3]))
1852
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
4280
1853
encoding = str(err.error_args[0]) # encoding must always be a string
4281
1854
val = err.error_args[1]
4282
1855
start = int(err.error_args[2])