73
105
return self._client.call_with_body_bytes_expecting_body(
74
106
method, args, body_bytes)
75
except errors.ErrorFromSmartServer, err:
107
except errors.ErrorFromSmartServer as err:
76
108
self._translate_error(err, **err_context)
79
111
def response_tuple_to_repo_format(response):
80
112
"""Convert a response tuple describing a repository format to a format."""
81
113
format = RemoteRepositoryFormat()
82
format._rich_root_data = (response[0] == 'yes')
83
format._supports_tree_reference = (response[1] == 'yes')
84
format._supports_external_lookups = (response[2] == 'yes')
114
format._rich_root_data = (response[0] == b'yes')
115
format._supports_tree_reference = (response[1] == b'yes')
116
format._supports_external_lookups = (response[2] == b'yes')
85
117
format._network_name = response[3]
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
121
# Note that RemoteBzrDirProber lives in breezy.bzrdir so breezy.bzr.remote
122
# does not have to be imported unless a remote format is involved.
124
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
125
"""Format representing bzrdirs accessed via a smart server"""
127
supports_workingtrees = False
129
colocated_branches = False
132
_mod_bzrdir.BzrDirMetaFormat1.__init__(self)
133
# XXX: It's a bit ugly that the network name is here, because we'd
134
# like to believe that format objects are stateless or at least
135
# immutable, However, we do at least avoid mutating the name after
136
# it's returned. See <https://bugs.launchpad.net/bzr/+bug/504102>
137
self._network_name = None
140
return "%s(_network_name=%r)" % (self.__class__.__name__,
143
def get_format_description(self):
144
if self._network_name:
146
real_format = controldir.network_format_registry.get(
151
return 'Remote: ' + real_format.get_format_description()
152
return 'bzr remote bzrdir'
154
def get_format_string(self):
155
raise NotImplementedError(self.get_format_string)
157
def network_name(self):
158
if self._network_name:
159
return self._network_name
161
raise AssertionError("No network name set.")
163
def initialize_on_transport(self, transport):
165
# hand off the request to the smart server
166
client_medium = transport.get_smart_medium()
167
except errors.NoSmartMedium:
168
# TODO: lookup the local format from a server hint.
169
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
170
return local_dir_format.initialize_on_transport(transport)
171
client = _SmartClient(client_medium)
172
path = client.remote_path_from_transport(transport)
174
response = client.call(b'BzrDirFormat.initialize', path)
175
except errors.ErrorFromSmartServer as err:
176
_translate_error(err, path=path)
177
if response[0] != b'ok':
178
raise errors.SmartProtocolError('unexpected response code %s' % (response,))
179
format = RemoteBzrDirFormat()
180
self._supply_sub_formats_to(format)
181
return RemoteBzrDir(transport, format)
183
def parse_NoneTrueFalse(self, arg):
190
raise AssertionError("invalid arg %r" % arg)
192
def _serialize_NoneTrueFalse(self, arg):
199
def _serialize_NoneString(self, arg):
202
def initialize_on_transport_ex(self, transport, use_existing_dir=False,
203
create_prefix=False, force_new_repo=False, stacked_on=None,
204
stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
207
# hand off the request to the smart server
208
client_medium = transport.get_smart_medium()
209
except errors.NoSmartMedium:
212
# Decline to open it if the server doesn't support our required
213
# version (3) so that the VFS-based transport will do it.
214
if client_medium.should_probe():
216
server_version = client_medium.protocol_version()
217
if server_version != '2':
221
except errors.SmartProtocolError:
222
# Apparently there's no usable smart server there, even though
223
# the medium supports the smart protocol.
228
client = _SmartClient(client_medium)
229
path = client.remote_path_from_transport(transport)
230
if client_medium._is_remote_before((1, 16)):
233
# TODO: lookup the local format from a server hint.
234
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
235
self._supply_sub_formats_to(local_dir_format)
236
return local_dir_format.initialize_on_transport_ex(transport,
237
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
238
force_new_repo=force_new_repo, stacked_on=stacked_on,
239
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
240
make_working_trees=make_working_trees, shared_repo=shared_repo,
242
return self._initialize_on_transport_ex_rpc(client, path, transport,
243
use_existing_dir, create_prefix, force_new_repo, stacked_on,
244
stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
246
def _initialize_on_transport_ex_rpc(self, client, path, transport,
247
use_existing_dir, create_prefix, force_new_repo, stacked_on,
248
stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
250
args.append(self._serialize_NoneTrueFalse(use_existing_dir))
251
args.append(self._serialize_NoneTrueFalse(create_prefix))
252
args.append(self._serialize_NoneTrueFalse(force_new_repo))
253
args.append(self._serialize_NoneString(stacked_on))
254
# stack_on_pwd is often/usually our transport
257
stack_on_pwd = transport.relpath(stack_on_pwd).encode('utf-8')
260
except errors.PathNotChild:
262
args.append(self._serialize_NoneString(stack_on_pwd))
263
args.append(self._serialize_NoneString(repo_format_name))
264
args.append(self._serialize_NoneTrueFalse(make_working_trees))
265
args.append(self._serialize_NoneTrueFalse(shared_repo))
266
request_network_name = self._network_name or \
267
_mod_bzrdir.BzrDirFormat.get_default_format().network_name()
269
response = client.call(b'BzrDirFormat.initialize_ex_1.16',
270
request_network_name, path, *args)
271
except errors.UnknownSmartMethod:
272
client._medium._remember_remote_is_before((1, 16))
273
local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
274
self._supply_sub_formats_to(local_dir_format)
275
return local_dir_format.initialize_on_transport_ex(transport,
276
use_existing_dir=use_existing_dir, create_prefix=create_prefix,
277
force_new_repo=force_new_repo, stacked_on=stacked_on,
278
stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
279
make_working_trees=make_working_trees, shared_repo=shared_repo,
281
except errors.ErrorFromSmartServer as err:
282
_translate_error(err, path=path.decode('utf-8'))
283
repo_path = response[0]
284
bzrdir_name = response[6]
285
require_stacking = response[7]
286
require_stacking = self.parse_NoneTrueFalse(require_stacking)
287
format = RemoteBzrDirFormat()
288
format._network_name = bzrdir_name
289
self._supply_sub_formats_to(format)
290
bzrdir = RemoteBzrDir(transport, format, _client=client)
292
repo_format = response_tuple_to_repo_format(response[1:])
293
if repo_path == b'.':
295
repo_path = repo_path.decode('utf-8')
297
repo_bzrdir_format = RemoteBzrDirFormat()
298
repo_bzrdir_format._network_name = response[5]
299
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
303
final_stack = response[8] or None
305
final_stack = final_stack.decode('utf-8')
306
final_stack_pwd = response[9] or None
308
final_stack_pwd = urlutils.join(
309
transport.base, final_stack_pwd.decode('utf-8'))
310
remote_repo = RemoteRepository(repo_bzr, repo_format)
311
if len(response) > 10:
312
# Updated server verb that locks remotely.
313
repo_lock_token = response[10] or None
314
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
316
remote_repo.dont_leave_lock_in_place()
318
remote_repo.lock_write()
319
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
320
final_stack, final_stack_pwd, require_stacking)
321
policy.acquire_repository()
325
bzrdir._format.set_branch_format(self.get_branch_format())
327
# The repo has already been created, but we need to make sure that
328
# we'll make a stackable branch.
329
bzrdir._format.require_stacking(_skip_repo=True)
330
return remote_repo, bzrdir, require_stacking, policy
332
def _open(self, transport):
333
return RemoteBzrDir(transport, self)
335
def __eq__(self, other):
336
if not isinstance(other, RemoteBzrDirFormat):
338
return self.get_format_description() == other.get_format_description()
340
def __return_repository_format(self):
341
# Always return a RemoteRepositoryFormat object, but if a specific bzr
342
# repository format has been asked for, tell the RemoteRepositoryFormat
343
# that it should use that for init() etc.
344
result = RemoteRepositoryFormat()
345
custom_format = getattr(self, '_repository_format', None)
347
if isinstance(custom_format, RemoteRepositoryFormat):
350
# We will use the custom format to create repositories over the
351
# wire; expose its details like rich_root_data for code to
353
result._custom_format = custom_format
356
def get_branch_format(self):
357
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
358
if not isinstance(result, RemoteBranchFormat):
359
new_result = RemoteBranchFormat()
360
new_result._custom_format = result
362
self.set_branch_format(new_result)
366
repository_format = property(__return_repository_format,
367
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
370
class RemoteControlStore(_mod_config.IniFileStore):
371
"""Control store which attempts to use HPSS calls to retrieve control store.
373
Note that this is specific to bzr-based formats.
376
def __init__(self, bzrdir):
377
super(RemoteControlStore, self).__init__()
378
self.controldir = bzrdir
379
self._real_store = None
381
def lock_write(self, token=None):
383
return self._real_store.lock_write(token)
387
return self._real_store.unlock()
390
with self.lock_write():
391
# We need to be able to override the undecorated implementation
392
self.save_without_locking()
394
def save_without_locking(self):
395
super(RemoteControlStore, self).save()
397
def _ensure_real(self):
398
self.controldir._ensure_real()
399
if self._real_store is None:
400
self._real_store = _mod_config.ControlStore(self.controldir)
402
def external_url(self):
403
return urlutils.join(self.branch.user_url, 'control.conf')
405
def _load_content(self):
406
medium = self.controldir._client._medium
407
path = self.controldir._path_for_remote_call(self.controldir._client)
409
response, handler = self.controldir._call_expecting_body(
410
b'BzrDir.get_config_file', path)
411
except errors.UnknownSmartMethod:
413
return self._real_store._load_content()
414
if len(response) and response[0] != b'ok':
415
raise errors.UnexpectedSmartServerResponse(response)
416
return handler.read_body_bytes()
418
def _save_content(self, content):
419
# FIXME JRV 2011-11-22: Ideally this should use a
420
# HPSS call too, but at the moment it is not possible
421
# to write lock control directories.
423
return self._real_store._save_content(content)
426
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
92
427
"""Control directory on a remote server, accessed via bzr:// or similar."""
94
429
def __init__(self, transport, format, _client=None, _force_probe=False):
186
563
medium = self._client._medium
187
564
if medium._is_remote_before((1, 13)):
188
565
return self._vfs_cloning_metadir(require_stacking=require_stacking)
189
verb = 'BzrDir.cloning_metadir'
566
verb = b'BzrDir.cloning_metadir'
190
567
if require_stacking:
194
571
path = self._path_for_remote_call(self._client)
196
573
response = self._call(verb, path, stacking)
197
574
except errors.UnknownSmartMethod:
198
575
medium._remember_remote_is_before((1, 13))
199
576
return self._vfs_cloning_metadir(require_stacking=require_stacking)
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
577
except errors.UnknownErrorFromSmartServer as err:
578
if err.error_tuple != (b'BranchReference',):
203
580
# We need to resolve the branch reference to determine the
204
581
# cloning_metadir. This causes unnecessary RPCs to open the
205
582
# referenced branch (and bzrdir, etc) but only when the caller
206
583
# didn't already resolve the branch reference.
207
584
referenced_branch = self.open_branch()
208
return referenced_branch.bzrdir.cloning_metadir()
585
return referenced_branch.controldir.cloning_metadir()
209
586
if len(response) != 3:
210
587
raise errors.UnexpectedSmartServerResponse(response)
211
588
control_name, repo_name, branch_info = response
212
589
if len(branch_info) != 2:
213
590
raise errors.UnexpectedSmartServerResponse(response)
214
591
branch_ref, branch_name = branch_info
215
format = bzrdir.network_format_registry.get(control_name)
593
format = controldir.network_format_registry.get(control_name)
595
raise errors.UnknownFormatError(kind='control', format=control_name)
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
599
format.repository_format = _mod_repository.network_format_registry.get(
602
raise errors.UnknownFormatError(kind='repository',
604
if branch_ref == b'ref':
220
605
# XXX: we need possible_transports here to avoid reopening the
221
606
# connection to the referenced location
222
ref_bzrdir = BzrDir.open(branch_name)
607
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
223
608
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
224
609
format.set_branch_format(branch_format)
225
elif branch_ref == 'branch':
610
elif branch_ref == b'branch':
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
613
branch_format = branch.network_format_registry.get(
616
raise errors.UnknownFormatError(kind='branch',
618
format.set_branch_format(branch_format)
230
620
raise errors.UnexpectedSmartServerResponse(response)
266
672
def destroy_branch(self, name=None):
267
673
"""See BzrDir.destroy_branch"""
269
self._real_bzrdir.destroy_branch(name=name)
675
name = self._get_selected_branch()
677
raise errors.NoColocatedBranchSupport(self)
678
path = self._path_for_remote_call(self._client)
684
response = self._call(b'BzrDir.destroy_branch', path, *args)
685
except errors.UnknownSmartMethod:
687
self._real_bzrdir.destroy_branch(name=name)
688
self._next_open_branch_result = None
270
690
self._next_open_branch_result = None
691
if response[0] != b'ok':
692
raise SmartProtocolError('unexpected response code %s' % (response,))
272
def create_workingtree(self, revision_id=None, from_branch=None):
694
def create_workingtree(self, revision_id=None, from_branch=None,
695
accelerator_tree=None, hardlink=False):
273
696
raise errors.NotLocalUrl(self.transport.base)
275
def find_branch_format(self):
698
def find_branch_format(self, name=None):
276
699
"""Find the branch 'format' for this bzrdir.
278
701
This might be a synthetic object for e.g. RemoteBranch and SVN.
280
b = self.open_branch()
703
b = self.open_branch(name=name)
283
def get_branch_reference(self):
706
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
707
path = self._path_for_remote_call(self._client)
709
response, handler = self._call_expecting_body(
710
b'BzrDir.get_branches', path)
711
except errors.UnknownSmartMethod:
713
return self._real_bzrdir.get_branches()
714
if response[0] != b"success":
715
raise errors.UnexpectedSmartServerResponse(response)
716
body = bencode.bdecode(handler.read_body_bytes())
718
for name, value in viewitems(body):
719
name = name.decode('utf-8')
720
ret[name] = self._open_branch(name, value[0], value[1],
721
possible_transports=possible_transports,
722
ignore_fallbacks=ignore_fallbacks)
725
def set_branch_reference(self, target_branch, name=None):
726
"""See BzrDir.set_branch_reference()."""
728
name = self._get_selected_branch()
730
raise errors.NoColocatedBranchSupport(self)
732
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
734
def get_branch_reference(self, name=None):
284
735
"""See BzrDir.get_branch_reference()."""
737
name = self._get_selected_branch()
739
raise errors.NoColocatedBranchSupport(self)
285
740
response = self._get_branch_reference()
286
741
if response[0] == 'ref':
742
return response[1].decode('utf-8')
291
746
def _get_branch_reference(self):
747
"""Get branch reference information
749
:return: Tuple with (kind, location_or_format)
750
if kind == 'ref', then location_or_format contains a location
751
otherwise, it contains a format name
292
753
path = self._path_for_remote_call(self._client)
293
754
medium = self._client._medium
294
755
candidate_calls = [
295
('BzrDir.open_branchV3', (2, 1)),
296
('BzrDir.open_branchV2', (1, 13)),
297
('BzrDir.open_branch', None),
756
(b'BzrDir.open_branchV3', (2, 1)),
757
(b'BzrDir.open_branchV2', (1, 13)),
758
(b'BzrDir.open_branch', None),
299
760
for verb, required_version in candidate_calls:
300
761
if required_version and medium._is_remote_before(required_version):
307
768
medium._remember_remote_is_before(required_version)
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
771
if verb == b'BzrDir.open_branch':
772
if response[0] != b'ok':
312
773
raise errors.UnexpectedSmartServerResponse(response)
313
if response[1] != '':
774
if response[1] != b'':
314
775
return ('ref', response[1])
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
777
return ('branch', b'')
778
if response[0] not in (b'ref', b'branch'):
318
779
raise errors.UnexpectedSmartServerResponse(response)
780
return (response[0].decode('ascii'), response[1])
321
def _get_tree_branch(self):
782
def _get_tree_branch(self, name=None):
322
783
"""See BzrDir._get_tree_branch()."""
323
return None, self.open_branch()
784
return None, self.open_branch(name=name)
325
def open_branch(self, name=None, unsupported=False,
326
ignore_fallbacks=False):
328
raise NotImplementedError('unsupported flag support not implemented yet.')
329
if self._next_open_branch_result is not None:
330
# See create_branch for details.
331
result = self._next_open_branch_result
332
self._next_open_branch_result = None
334
response = self._get_branch_reference()
335
if response[0] == 'ref':
786
def _open_branch(self, name, kind, location_or_format,
787
ignore_fallbacks=False, possible_transports=None):
336
789
# a branch reference, use the existing BranchReference logic.
337
790
format = BranchReferenceFormat()
338
791
return format.open(self, name=name, _found=True,
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
792
location=location_or_format.decode('utf-8'),
793
ignore_fallbacks=ignore_fallbacks,
794
possible_transports=possible_transports)
795
branch_format_name = location_or_format
341
796
if not branch_format_name:
342
797
branch_format_name = None
343
798
format = RemoteBranchFormat(network_name=branch_format_name)
344
799
return RemoteBranch(self, self.find_repository(), format=format,
345
setup_stacking=not ignore_fallbacks, name=name)
800
setup_stacking=not ignore_fallbacks, name=name,
801
possible_transports=possible_transports)
803
def open_branch(self, name=None, unsupported=False,
804
ignore_fallbacks=False, possible_transports=None):
806
name = self._get_selected_branch()
808
raise errors.NoColocatedBranchSupport(self)
810
raise NotImplementedError('unsupported flag support not implemented yet.')
811
if self._next_open_branch_result is not None:
812
# See create_branch for details.
813
result = self._next_open_branch_result
814
self._next_open_branch_result = None
816
response = self._get_branch_reference()
817
return self._open_branch(name, response[0], response[1],
818
possible_transports=possible_transports,
819
ignore_fallbacks=ignore_fallbacks)
347
821
def _open_repo_v1(self, path):
348
verb = 'BzrDir.find_repository'
822
verb = b'BzrDir.find_repository'
349
823
response = self._call(verb, path)
350
if response[0] != 'ok':
824
if response[0] != b'ok':
351
825
raise errors.UnexpectedSmartServerResponse(response)
352
826
# servers that only support the v1 method don't support external
353
827
# references either.
354
828
self._ensure_real()
355
829
repo = self._real_bzrdir.open_repository()
356
response = response + ('no', repo._format.network_name())
830
response = response + (b'no', repo._format.network_name())
357
831
return response, repo
359
833
def _open_repo_v2(self, path):
360
verb = 'BzrDir.find_repositoryV2'
834
verb = b'BzrDir.find_repositoryV2'
361
835
response = self._call(verb, path)
362
if response[0] != 'ok':
836
if response[0] != b'ok':
363
837
raise errors.UnexpectedSmartServerResponse(response)
364
838
self._ensure_real()
365
839
repo = self._real_bzrdir.open_repository()
441
930
"""Upgrading of remote bzrdirs is not supported yet."""
444
def needs_format_conversion(self, format=None):
933
def needs_format_conversion(self, format):
445
934
"""Upgrading of remote bzrdirs is not supported yet."""
447
symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
448
% 'needs_format_conversion(format=None)')
451
def clone(self, url, revision_id=None, force_new_repo=False,
452
preserve_stacking=False):
454
return self._real_bzrdir.clone(url, revision_id=revision_id,
455
force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
457
937
def _get_config(self):
458
938
return RemoteBzrDirConfig(self)
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
940
def _get_config_store(self):
941
return RemoteControlStore(self)
944
class RemoteInventoryTree(InventoryRevisionTree):
946
def __init__(self, repository, inv, revision_id):
947
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
949
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
950
ret = self._repository._revision_archive(
951
self.get_revision_id(), format, name, root, subdir,
952
force_mtime=force_mtime)
954
return super(RemoteInventoryTree, self).archive(
955
format, name, root, subdir, force_mtime=force_mtime)
958
def annotate_iter(self, path, file_id=None,
959
default_revision=_mod_revision.CURRENT_REVISION):
960
"""Return an iterator of revision_id, line tuples.
962
For working trees (and mutable trees in general), the special
963
revision_id 'current:' will be used for lines that are new in this
964
tree, e.g. uncommitted changes.
965
:param file_id: The file to produce an annotated version from
966
:param default_revision: For lines that don't match a basis, mark them
967
with this revision id. Not all implementations will make use of
970
ret = self._repository._annotate_file_revision(
971
self.get_revision_id(), path, file_id, default_revision)
973
return super(RemoteInventoryTree, self).annotate_iter(
974
path, file_id, default_revision=default_revision)
978
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
462
979
"""Format for repositories accessed over a _SmartClient.
464
981
Instances of this repository are represented by RemoteRepository
529
1068
self._custom_format.supports_tree_reference
530
1069
return self._supports_tree_reference
532
def _vfs_initialize(self, a_bzrdir, shared):
1072
def revision_graph_can_have_wrong_parents(self):
1073
if self._revision_graph_can_have_wrong_parents is None:
1075
self._revision_graph_can_have_wrong_parents = \
1076
self._custom_format.revision_graph_can_have_wrong_parents
1077
return self._revision_graph_can_have_wrong_parents
1079
def _vfs_initialize(self, a_controldir, shared):
533
1080
"""Helper for common code in initialize."""
534
1081
if self._custom_format:
535
1082
# Custom format requested
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1083
result = self._custom_format.initialize(a_controldir, shared=shared)
537
1084
elif self._creating_bzrdir is not None:
538
1085
# Use the format that the repository we were created to back
540
1087
prior_repo = self._creating_bzrdir.open_repository()
541
1088
prior_repo._ensure_real()
542
1089
result = prior_repo._real_repository._format.initialize(
543
a_bzrdir, shared=shared)
1090
a_controldir, shared=shared)
545
1092
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
546
1093
# support remote initialization.
547
1094
# We delegate to a real object at this point (as RemoteBzrDir
548
1095
# delegate to the repository format which would lead to infinite
549
# recursion if we just called a_bzrdir.create_repository.
550
a_bzrdir._ensure_real()
551
result = a_bzrdir._real_bzrdir.create_repository(shared=shared)
1096
# recursion if we just called a_controldir.create_repository.
1097
a_controldir._ensure_real()
1098
result = a_controldir._real_bzrdir.create_repository(shared=shared)
552
1099
if not isinstance(result, RemoteRepository):
553
return self.open(a_bzrdir)
1100
return self.open(a_controldir)
557
def initialize(self, a_bzrdir, shared=False):
1104
def initialize(self, a_controldir, shared=False):
558
1105
# Being asked to create on a non RemoteBzrDir:
559
if not isinstance(a_bzrdir, RemoteBzrDir):
560
return self._vfs_initialize(a_bzrdir, shared)
561
medium = a_bzrdir._client._medium
1106
if not isinstance(a_controldir, RemoteBzrDir):
1107
return self._vfs_initialize(a_controldir, shared)
1108
medium = a_controldir._client._medium
562
1109
if medium._is_remote_before((1, 13)):
563
return self._vfs_initialize(a_bzrdir, shared)
1110
return self._vfs_initialize(a_controldir, shared)
564
1111
# Creating on a remote bzr dir.
565
1112
# 1) get the network name to use.
566
1113
if self._custom_format:
568
1115
elif self._network_name:
569
1116
network_name = self._network_name
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1118
# Select the current breezy default and ask for that.
1119
reference_bzrdir_format = controldir.format_registry.get('default')()
573
1120
reference_format = reference_bzrdir_format.repository_format
574
1121
network_name = reference_format.network_name()
575
1122
# 2) try direct creation via RPC
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1123
path = a_controldir._path_for_remote_call(a_controldir._client)
1124
verb = b'BzrDir.create_repository'
1126
shared_str = b'True'
1128
shared_str = b'False'
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1130
response = a_controldir._call(verb, path, network_name, shared_str)
584
1131
except errors.UnknownSmartMethod:
585
1132
# Fallback - use vfs methods
586
1133
medium._remember_remote_is_before((1, 13))
587
return self._vfs_initialize(a_bzrdir, shared)
1134
return self._vfs_initialize(a_controldir, shared)
589
1136
# Turn the response into a RemoteRepository object.
590
1137
format = response_tuple_to_repo_format(response[1:])
591
1138
# Used to support creating a real format instance when needed.
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1139
format._creating_bzrdir = a_controldir
1140
remote_repo = RemoteRepository(a_controldir, format)
594
1141
format._creating_repo = remote_repo
595
1142
return remote_repo
597
def open(self, a_bzrdir):
598
if not isinstance(a_bzrdir, RemoteBzrDir):
599
raise AssertionError('%r is not a RemoteBzrDir' % (a_bzrdir,))
600
return a_bzrdir.open_repository()
1144
def open(self, a_controldir):
1145
if not isinstance(a_controldir, RemoteBzrDir):
1146
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1147
return a_controldir.open_repository()
602
1149
def _ensure_real(self):
603
1150
if self._custom_format is None:
604
self._custom_format = repository.network_format_registry.get(
1152
self._custom_format = _mod_repository.network_format_registry.get(
1155
raise errors.UnknownFormatError(kind='repository',
1156
format=self._network_name)
608
1159
def _fetch_order(self):
910
1509
# one; unfortunately the tests rely on slightly different behaviour at
911
1510
# present -- mbp 20090710
912
1511
return (self.__class__ is other.__class__ and
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1512
self.controldir.transport.base == other.controldir.transport.base)
915
1514
def get_graph(self, other_repository=None):
916
1515
"""Return the graph for this repository format"""
917
1516
parents_provider = self._make_parents_provider(other_repository)
918
1517
return graph.Graph(parents_provider)
921
1519
def get_known_graph_ancestry(self, revision_ids):
922
1520
"""Return the known graph for a set of revision ids and their ancestors.
924
st = static_tuple.StaticTuple
925
revision_keys = [st(r_id).intern() for r_id in revision_ids]
926
known_graph = self.revisions.get_known_graph_ancestry(revision_keys)
927
return graph.GraphThunkIdsToKeys(known_graph)
1522
with self.lock_read():
1523
revision_graph = dict(((key, value) for key, value in
1524
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1525
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1526
return graph.KnownGraph(revision_graph)
929
1528
def gather_stats(self, revid=None, committers=None):
930
1529
"""See Repository.gather_stats()."""
931
path = self.bzrdir._path_for_remote_call(self._client)
1530
path = self.controldir._path_for_remote_call(self._client)
932
1531
# revid can be None to indicate no revisions, not just NULL_REVISION
933
if revid is None or revision.is_null(revid):
1532
if revid is None or _mod_revision.is_null(revid):
936
1535
fmt_revid = revid
937
1536
if committers is None or not committers:
938
fmt_committers = 'no'
1537
fmt_committers = b'no'
940
fmt_committers = 'yes'
1539
fmt_committers = b'yes'
941
1540
response_tuple, response_handler = self._call_expecting_body(
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1541
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1542
if response_tuple[0] != b'ok':
944
1543
raise errors.UnexpectedSmartServerResponse(response_tuple)
946
1545
body = response_handler.read_body_bytes()
948
for line in body.split('\n'):
1547
for line in body.split(b'\n'):
951
key, val_text = line.split(':')
1550
key, val_text = line.split(b':')
1551
key = key.decode('ascii')
952
1552
if key in ('revisions', 'size', 'committers'):
953
1553
result[key] = int(val_text)
954
1554
elif key in ('firstrev', 'latestrev'):
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1555
values = val_text.split(b' ')[1:]
1556
result[key] = (float(values[0]), int(values[1]))
1195
1835
raise errors.UnexpectedSmartServerResponse(response)
1197
1837
def sprout(self, to_bzrdir, revision_id=None):
1198
# TODO: Option to control what format is created?
1200
dest_repo = self._real_repository._format.initialize(to_bzrdir,
1202
dest_repo.fetch(self, revision_id=revision_id)
1838
"""Create a descendent repository for new development.
1840
Unlike clone, this does not copy the settings of the repository.
1842
with self.lock_read():
1843
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1844
dest_repo.fetch(self, revision_id=revision_id)
1847
def _create_sprouting_repo(self, a_controldir, shared):
1848
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1849
# use target default format.
1850
dest_repo = a_controldir.create_repository()
1852
# Most control formats need the repository to be specifically
1853
# created, but on some old all-in-one formats it's not needed
1855
dest_repo = self._format.initialize(a_controldir, shared=shared)
1856
except errors.UninitializableFormat:
1857
dest_repo = a_controldir.open_repository()
1203
1858
return dest_repo
1205
1860
### These methods are just thin shims to the VFS object for now.
1207
1862
def revision_tree(self, revision_id):
1209
return self._real_repository.revision_tree(revision_id)
1863
with self.lock_read():
1864
revision_id = _mod_revision.ensure_null(revision_id)
1865
if revision_id == _mod_revision.NULL_REVISION:
1866
return InventoryRevisionTree(self,
1867
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1869
return list(self.revision_trees([revision_id]))[0]
1211
1871
def get_serializer_format(self):
1213
return self._real_repository.get_serializer_format()
1872
path = self.controldir._path_for_remote_call(self._client)
1874
response = self._call(b'VersionedFileRepository.get_serializer_format',
1876
except errors.UnknownSmartMethod:
1878
return self._real_repository.get_serializer_format()
1879
if response[0] != b'ok':
1880
raise errors.UnexpectedSmartServerResponse(response)
1215
1883
def get_commit_builder(self, branch, parents, config, timestamp=None,
1216
1884
timezone=None, committer=None, revprops=None,
1218
# FIXME: It ought to be possible to call this without immediately
1219
# triggering _ensure_real. For now it's the easiest thing to do.
1221
real_repo = self._real_repository
1222
builder = real_repo.get_commit_builder(branch, parents,
1223
config, timestamp=timestamp, timezone=timezone,
1224
committer=committer, revprops=revprops, revision_id=revision_id)
1885
revision_id=None, lossy=False):
1886
"""Obtain a CommitBuilder for this repository.
1888
:param branch: Branch to commit to.
1889
:param parents: Revision ids of the parents of the new revision.
1890
:param config: Configuration to use.
1891
:param timestamp: Optional timestamp recorded for commit.
1892
:param timezone: Optional timezone for timestamp.
1893
:param committer: Optional committer to set for commit.
1894
:param revprops: Optional dictionary of revision properties.
1895
:param revision_id: Optional revision id.
1896
:param lossy: Whether to discard data that can not be natively
1897
represented, when pushing to a foreign VCS
1899
if self._fallback_repositories and not self._format.supports_chks:
1900
raise errors.BzrError("Cannot commit directly to a stacked branch"
1901
" in pre-2a formats. See "
1902
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1903
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1904
result = commit_builder_kls(self, parents, config,
1905
timestamp, timezone, committer, revprops, revision_id,
1907
self.start_write_group()
1227
1910
def add_fallback_repository(self, repository):
1228
1911
"""Add a repository to use for looking up data not held locally.
1272
1956
delta, new_revision_id, parents, basis_inv=basis_inv,
1273
1957
propagate_caches=propagate_caches)
1275
def add_revision(self, rev_id, rev, inv=None, config=None):
1277
return self._real_repository.add_revision(
1278
rev_id, rev, inv=inv, config=config)
1959
def add_revision(self, revision_id, rev, inv=None):
1960
_mod_revision.check_not_reserved_id(revision_id)
1961
key = (revision_id,)
1962
# check inventory present
1963
if not self.inventories.get_parent_map([key]):
1965
raise errors.WeaveRevisionNotPresent(revision_id,
1968
# yes, this is not suitable for adding with ghosts.
1969
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1972
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1973
self._add_revision(rev)
1975
def _add_revision(self, rev):
1976
if self._real_repository is not None:
1977
return self._real_repository._add_revision(rev)
1978
text = self._serializer.write_revision_to_string(rev)
1979
key = (rev.revision_id,)
1980
parents = tuple((parent,) for parent in rev.parent_ids)
1981
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1982
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1983
self._format, self._write_group_tokens)
1281
1985
def get_inventory(self, revision_id):
1986
with self.lock_read():
1987
return list(self.iter_inventories([revision_id]))[0]
1989
def _iter_inventories_rpc(self, revision_ids, ordering):
1990
if ordering is None:
1991
ordering = 'unordered'
1992
path = self.controldir._path_for_remote_call(self._client)
1993
body = b"\n".join(revision_ids)
1994
response_tuple, response_handler = (
1995
self._call_with_body_bytes_expecting_body(
1996
b"VersionedFileRepository.get_inventories",
1997
(path, ordering.encode('ascii')), body))
1998
if response_tuple[0] != b"ok":
1999
raise errors.UnexpectedSmartServerResponse(response_tuple)
2000
deserializer = inventory_delta.InventoryDeltaDeserializer()
2001
byte_stream = response_handler.read_streamed_body()
2002
decoded = smart_repo._byte_stream_to_stream(byte_stream)
2004
# no results whatsoever
2006
src_format, stream = decoded
2007
if src_format.network_name() != self._format.network_name():
2008
raise AssertionError(
2009
"Mismatched RemoteRepository and stream src %r, %r" % (
2010
src_format.network_name(), self._format.network_name()))
2011
# ignore the src format, it's not really relevant
2012
prev_inv = Inventory(root_id=None,
2013
revision_id=_mod_revision.NULL_REVISION)
2014
# there should be just one substream, with inventory deltas
2016
substream_kind, substream = next(stream)
2017
except StopIteration:
2019
if substream_kind != "inventory-deltas":
2020
raise AssertionError(
2021
"Unexpected stream %r received" % substream_kind)
2022
for record in substream:
2023
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2024
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
2025
if parent_id != prev_inv.revision_id:
2026
raise AssertionError("invalid base %r != %r" % (parent_id,
2027
prev_inv.revision_id))
2028
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2029
yield inv, inv.revision_id
2032
def _iter_inventories_vfs(self, revision_ids, ordering=None):
1282
2033
self._ensure_real()
1283
return self._real_repository.get_inventory(revision_id)
2034
return self._real_repository._iter_inventories(revision_ids, ordering)
1285
2036
def iter_inventories(self, revision_ids, ordering=None):
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2037
"""Get many inventories by revision_ids.
2039
This will buffer some or all of the texts used in constructing the
2040
inventories in memory, but will only parse a single inventory at a
2043
:param revision_ids: The expected revision ids of the inventories.
2044
:param ordering: optional ordering, e.g. 'topological'. If not
2045
specified, the order of revision_ids will be preserved (by
2046
buffering if necessary).
2047
:return: An iterator of inventories.
2049
if ((None in revision_ids)
2050
or (_mod_revision.NULL_REVISION in revision_ids)):
2051
raise ValueError('cannot get null revision inventory')
2052
for inv, revid in self._iter_inventories(revision_ids, ordering):
2054
raise errors.NoSuchRevision(self, revid)
2057
def _iter_inventories(self, revision_ids, ordering=None):
2058
if len(revision_ids) == 0:
2060
missing = set(revision_ids)
2061
if ordering is None:
2062
order_as_requested = True
2064
order = list(revision_ids)
2066
next_revid = order.pop()
2068
order_as_requested = False
2069
if ordering != 'unordered' and self._fallback_repositories:
2070
raise ValueError('unsupported ordering %r' % ordering)
2071
iter_inv_fns = [self._iter_inventories_rpc] + [
2072
fallback._iter_inventories for fallback in
2073
self._fallback_repositories]
2075
for iter_inv in iter_inv_fns:
2076
request = [revid for revid in revision_ids if revid in missing]
2077
for inv, revid in iter_inv(request, ordering):
2080
missing.remove(inv.revision_id)
2081
if ordering != 'unordered':
2085
if order_as_requested:
2086
# Yield as many results as we can while preserving order.
2087
while next_revid in invs:
2088
inv = invs.pop(next_revid)
2089
yield inv, inv.revision_id
2091
next_revid = order.pop()
2093
# We still want to fully consume the stream, just
2094
# in case it is not actually finished at this point
2097
except errors.UnknownSmartMethod:
2098
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2102
if order_as_requested:
2103
if next_revid is not None:
2104
yield None, next_revid
2107
yield invs.get(revid), revid
2110
yield None, missing.pop()
1290
2112
def get_revision(self, revision_id):
1292
return self._real_repository.get_revision(revision_id)
2113
with self.lock_read():
2114
return self.get_revisions([revision_id])[0]
1294
2116
def get_transaction(self):
1295
2117
self._ensure_real()
1296
2118
return self._real_repository.get_transaction()
1299
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2120
def clone(self, a_controldir, revision_id=None):
2121
with self.lock_read():
2122
dest_repo = self._create_sprouting_repo(
2123
a_controldir, shared=self.is_shared())
2124
self.copy_content_into(dest_repo, revision_id)
1303
2127
def make_working_trees(self):
1304
2128
"""See Repository.make_working_trees"""
1306
return self._real_repository.make_working_trees()
2129
path = self.controldir._path_for_remote_call(self._client)
2131
response = self._call(b'Repository.make_working_trees', path)
2132
except errors.UnknownSmartMethod:
2134
return self._real_repository.make_working_trees()
2135
if response[0] not in (b'yes', b'no'):
2136
raise SmartProtocolError('unexpected response code %s' % (response,))
2137
return response[0] == b'yes'
1308
2139
def refresh_data(self):
1309
"""Re-read any data needed to to synchronise with disk.
2140
"""Re-read any data needed to synchronise with disk.
1311
2142
This method is intended to be called after another repository instance
1312
2143
(such as one used by a smart server) has inserted data into the
1313
repository. It may not be called during a write group, but may be
1314
called at any other time.
2144
repository. On all repositories this will work outside of write groups.
2145
Some repository formats (pack and newer for breezy native formats)
2146
support refresh_data inside write groups. If called inside a write
2147
group on a repository that does not support refreshing in a write group
2148
IsInWriteGroupError will be raised.
1316
if self.is_in_write_group():
1317
raise errors.InternalBzrError(
1318
"May not refresh_data while in a write group.")
1319
2150
if self._real_repository is not None:
1320
2151
self._real_repository.refresh_data()
2152
# Refresh the parents cache for this object
2153
self._unstacked_provider.disable_cache()
2154
self._unstacked_provider.enable_cache()
1322
2156
def revision_ids_to_search_result(self, result_set):
1323
2157
"""Convert a set of revision ids to a graph SearchResult."""
1324
2158
result_parents = set()
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2159
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1327
2160
result_parents.update(parents)
1328
2161
included_keys = result_set.intersection(result_parents)
1329
2162
start_keys = result_set.difference(included_keys)
1330
2163
exclude_keys = result_parents.difference(result_set)
1331
result = graph.SearchResult(start_keys, exclude_keys,
2164
result = vf_search.SearchResult(start_keys, exclude_keys,
1332
2165
len(result_set), result_set)
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2168
def search_missing_revision_ids(self, other,
2169
find_ghosts=True, revision_ids=None, if_present_ids=None,
1337
2171
"""Return the revision ids that other has that this does not.
1339
2173
These are returned in topological order.
1341
2175
revision_id: only return revision ids included by revision_id.
1343
return repository.InterRepository.get(
1344
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2177
with self.lock_read():
2178
inter_repo = _mod_repository.InterRepository.get(other, self)
2179
return inter_repo.search_missing_revision_ids(
2180
find_ghosts=find_ghosts, revision_ids=revision_ids,
2181
if_present_ids=if_present_ids, limit=limit)
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2183
def fetch(self, source, revision_id=None, find_ghosts=False,
1347
2184
fetch_spec=None):
1348
2185
# No base implementation to use as RemoteRepository is not a subclass
1349
2186
# of Repository; so this is a copy of Repository.fetch().
1388
2224
return self._real_repository._get_versioned_file_checker(
1389
2225
revisions, revision_versions_cache)
2227
def _iter_files_bytes_rpc(self, desired_files, absent):
2228
path = self.controldir._path_for_remote_call(self._client)
2231
for (file_id, revid, identifier) in desired_files:
2232
lines.append(b''.join([
2233
osutils.safe_file_id(file_id),
2235
osutils.safe_revision_id(revid)]))
2236
identifiers.append(identifier)
2237
(response_tuple, response_handler) = (
2238
self._call_with_body_bytes_expecting_body(
2239
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2240
if response_tuple != (b'ok', ):
2241
response_handler.cancel_read_body()
2242
raise errors.UnexpectedSmartServerResponse(response_tuple)
2243
byte_stream = response_handler.read_streamed_body()
2244
def decompress_stream(start, byte_stream, unused):
2245
decompressor = zlib.decompressobj()
2246
yield decompressor.decompress(start)
2247
while decompressor.unused_data == b"":
2249
data = next(byte_stream)
2250
except StopIteration:
2252
yield decompressor.decompress(data)
2253
yield decompressor.flush()
2254
unused.append(decompressor.unused_data)
2257
while not b"\n" in unused:
2259
unused += next(byte_stream)
2260
except StopIteration:
2262
header, rest = unused.split(b"\n", 1)
2263
args = header.split(b"\0")
2264
if args[0] == b"absent":
2265
absent[identifiers[int(args[3])]] = (args[1], args[2])
2268
elif args[0] == b"ok":
2271
raise errors.UnexpectedSmartServerResponse(args)
2273
yield (identifiers[idx],
2274
decompress_stream(rest, byte_stream, unused_chunks))
2275
unused = b"".join(unused_chunks)
1391
2277
def iter_files_bytes(self, desired_files):
1392
2278
"""See Repository.iter_file_bytes.
1395
return self._real_repository.iter_files_bytes(desired_files)
2282
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2283
desired_files, absent):
2284
yield identifier, bytes_iterator
2285
for fallback in self._fallback_repositories:
2288
desired_files = [(key[0], key[1], identifier)
2289
for identifier, key in viewitems(absent)]
2290
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2291
del absent[identifier]
2292
yield identifier, bytes_iterator
2294
# There may be more missing items, but raise an exception
2296
missing_identifier = next(iter(absent))
2297
missing_key = absent[missing_identifier]
2298
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2299
file_id=missing_key[0])
2300
except errors.UnknownSmartMethod:
2302
for (identifier, bytes_iterator) in (
2303
self._real_repository.iter_files_bytes(desired_files)):
2304
yield identifier, bytes_iterator
2306
def get_cached_parent_map(self, revision_ids):
2307
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2308
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1397
2310
def get_parent_map(self, revision_ids):
1398
"""See bzrlib.Graph.get_parent_map()."""
2311
"""See breezy.Graph.get_parent_map()."""
1399
2312
return self._make_parents_provider().get_parent_map(revision_ids)
1401
2314
def _get_parent_map_rpc(self, keys):
1529
2431
revision_graph[d[0]] = (NULL_REVISION,)
1530
2432
return revision_graph
1533
2434
def get_signature_text(self, revision_id):
1535
return self._real_repository.get_signature_text(revision_id)
2435
with self.lock_read():
2436
path = self.controldir._path_for_remote_call(self._client)
2438
response_tuple, response_handler = self._call_expecting_body(
2439
b'Repository.get_revision_signature_text', path, revision_id)
2440
except errors.UnknownSmartMethod:
2442
return self._real_repository.get_signature_text(revision_id)
2443
except errors.NoSuchRevision as err:
2444
for fallback in self._fallback_repositories:
2446
return fallback.get_signature_text(revision_id)
2447
except errors.NoSuchRevision:
2451
if response_tuple[0] != b'ok':
2452
raise errors.UnexpectedSmartServerResponse(response_tuple)
2453
return response_handler.read_body_bytes()
1538
2455
def _get_inventory_xml(self, revision_id):
1540
return self._real_repository._get_inventory_xml(revision_id)
2456
with self.lock_read():
2457
# This call is used by older working tree formats,
2458
# which stored a serialized basis inventory.
2460
return self._real_repository._get_inventory_xml(revision_id)
1542
2462
def reconcile(self, other=None, thorough=False):
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2463
from ..reconcile import RepoReconciler
2464
with self.lock_write():
2465
path = self.controldir._path_for_remote_call(self._client)
2467
response, handler = self._call_expecting_body(
2468
b'Repository.reconcile', path, self._lock_token)
2469
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2471
return self._real_repository.reconcile(other=other, thorough=thorough)
2472
if response != (b'ok', ):
2473
raise errors.UnexpectedSmartServerResponse(response)
2474
body = handler.read_body_bytes()
2475
result = RepoReconciler(self)
2476
for line in body.split(b'\n'):
2479
key, val_text = line.split(b':')
2480
if key == b"garbage_inventories":
2481
result.garbage_inventories = int(val_text)
2482
elif key == b"inconsistent_parents":
2483
result.inconsistent_parents = int(val_text)
2485
mutter("unknown reconcile key %r" % key)
1546
2488
def all_revision_ids(self):
1548
return self._real_repository.all_revision_ids()
2489
path = self.controldir._path_for_remote_call(self._client)
2491
response_tuple, response_handler = self._call_expecting_body(
2492
b"Repository.all_revision_ids", path)
2493
except errors.UnknownSmartMethod:
2495
return self._real_repository.all_revision_ids()
2496
if response_tuple != (b"ok", ):
2497
raise errors.UnexpectedSmartServerResponse(response_tuple)
2498
revids = set(response_handler.read_body_bytes().splitlines())
2499
for fallback in self._fallback_repositories:
2500
revids.update(set(fallback.all_revision_ids()))
2503
def _filtered_revision_trees(self, revision_ids, file_ids):
2504
"""Return Tree for a revision on this branch with only some files.
2506
:param revision_ids: a sequence of revision-ids;
2507
a revision-id may not be None or b'null:'
2508
:param file_ids: if not None, the result is filtered
2509
so that only those file-ids, their parents and their
2510
children are included.
2512
inventories = self.iter_inventories(revision_ids)
2513
for inv in inventories:
2514
# Should we introduce a FilteredRevisionTree class rather
2515
# than pre-filter the inventory here?
2516
filtered_inv = inv.filter(file_ids)
2517
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1551
2519
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
1553
return self._real_repository.get_deltas_for_revisions(revisions,
1554
specific_fileids=specific_fileids)
2520
with self.lock_read():
2521
medium = self._client._medium
2522
if medium._is_remote_before((1, 2)):
2524
for delta in self._real_repository.get_deltas_for_revisions(
2525
revisions, specific_fileids):
2528
# Get the revision-ids of interest
2529
required_trees = set()
2530
for revision in revisions:
2531
required_trees.add(revision.revision_id)
2532
required_trees.update(revision.parent_ids[:1])
2534
# Get the matching filtered trees. Note that it's more
2535
# efficient to pass filtered trees to changes_from() rather
2536
# than doing the filtering afterwards. changes_from() could
2537
# arguably do the filtering itself but it's path-based, not
2538
# file-id based, so filtering before or afterwards is
2540
if specific_fileids is None:
2541
trees = dict((t.get_revision_id(), t) for
2542
t in self.revision_trees(required_trees))
2544
trees = dict((t.get_revision_id(), t) for
2545
t in self._filtered_revision_trees(required_trees,
2548
# Calculate the deltas
2549
for revision in revisions:
2550
if not revision.parent_ids:
2551
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2553
old_tree = trees[revision.parent_ids[0]]
2554
yield trees[revision.revision_id].changes_from(old_tree)
1557
2556
def get_revision_delta(self, revision_id, specific_fileids=None):
1559
return self._real_repository.get_revision_delta(revision_id,
1560
specific_fileids=specific_fileids)
2557
with self.lock_read():
2558
r = self.get_revision(revision_id)
2559
return list(self.get_deltas_for_revisions([r],
2560
specific_fileids=specific_fileids))[0]
1563
2562
def revision_trees(self, revision_ids):
1565
return self._real_repository.revision_trees(revision_ids)
2563
with self.lock_read():
2564
inventories = self.iter_inventories(revision_ids)
2565
for inv in inventories:
2566
yield RemoteInventoryTree(self, inv, inv.revision_id)
1568
2568
def get_revision_reconcile(self, revision_id):
1570
return self._real_repository.get_revision_reconcile(revision_id)
2569
with self.lock_read():
2571
return self._real_repository.get_revision_reconcile(revision_id)
1573
2573
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
1575
return self._real_repository.check(revision_ids=revision_ids,
1576
callback_refs=callback_refs, check_repo=check_repo)
2574
with self.lock_read():
2576
return self._real_repository.check(revision_ids=revision_ids,
2577
callback_refs=callback_refs, check_repo=check_repo)
1578
2579
def copy_content_into(self, destination, revision_id=None):
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2580
"""Make a complete copy of the content in self into destination.
2582
This is a destructive operation! Do not use it on existing
2585
interrepo = _mod_repository.InterRepository.get(self, destination)
2586
return interrepo.copy_content(revision_id)
1583
2588
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
1584
2589
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
1588
2592
# TODO: Maybe a progress bar while streaming the tarball?
1589
note("Copying repository content as tarball...")
2593
note(gettext("Copying repository content as tarball..."))
1590
2594
tar_file = self._get_tarball('bz2')
1591
2595
if tar_file is None:
1680
2694
self._ensure_real()
1681
2695
return self._real_repository.texts
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2697
def _iter_revisions_rpc(self, revision_ids):
2698
body = b"\n".join(revision_ids)
2699
path = self.controldir._path_for_remote_call(self._client)
2700
response_tuple, response_handler = (
2701
self._call_with_body_bytes_expecting_body(
2702
b"Repository.iter_revisions", (path, ), body))
2703
if response_tuple[0] != b"ok":
2704
raise errors.UnexpectedSmartServerResponse(response_tuple)
2705
serializer_format = response_tuple[1].decode('ascii')
2706
serializer = serializer_format_registry.get(serializer_format)
2707
byte_stream = response_handler.read_streamed_body()
2708
decompressor = zlib.decompressobj()
2710
for bytes in byte_stream:
2711
chunks.append(decompressor.decompress(bytes))
2712
if decompressor.unused_data != b"":
2713
chunks.append(decompressor.flush())
2714
yield serializer.read_revision_from_string(b"".join(chunks))
2715
unused = decompressor.unused_data
2716
decompressor = zlib.decompressobj()
2717
chunks = [decompressor.decompress(unused)]
2718
chunks.append(decompressor.flush())
2719
text = b"".join(chunks)
2721
yield serializer.read_revision_from_string(b"".join(chunks))
2723
def iter_revisions(self, revision_ids):
2724
for rev_id in revision_ids:
2725
if not rev_id or not isinstance(rev_id, bytes):
2726
raise errors.InvalidRevisionId(
2727
revision_id=rev_id, branch=self)
2728
with self.lock_read():
2730
missing = set(revision_ids)
2731
for rev in self._iter_revisions_rpc(revision_ids):
2732
missing.remove(rev.revision_id)
2733
yield (rev.revision_id, rev)
2734
for fallback in self._fallback_repositories:
2737
for (revid, rev) in fallback.iter_revisions(missing):
2740
missing.remove(revid)
2741
for revid in missing:
2743
except errors.UnknownSmartMethod:
2745
for entry in self._real_repository.iter_revisions(revision_ids):
1688
2748
def supports_rich_root(self):
1689
2749
return self._format.rich_root_data
1691
def iter_reverse_revision_history(self, revision_id):
1693
return self._real_repository.iter_reverse_revision_history(revision_id)
1696
2752
def _serializer(self):
1697
2753
return self._format._serializer
1699
2755
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2756
with self.lock_write():
2757
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2758
self.add_signature_text(revision_id, signature)
1704
2760
def add_signature_text(self, revision_id, signature):
1706
return self._real_repository.add_signature_text(revision_id, signature)
2761
if self._real_repository:
2762
# If there is a real repository the write group will
2763
# be in the real repository as well, so use that:
2765
return self._real_repository.add_signature_text(
2766
revision_id, signature)
2767
path = self.controldir._path_for_remote_call(self._client)
2768
response, handler = self._call_with_body_bytes_expecting_body(
2769
b'Repository.add_signature_text', (path, self._lock_token,
2771
tuple([token.encode('utf-8') for token in self._write_group_tokens]),
2773
handler.cancel_read_body()
2775
if response[0] != b'ok':
2776
raise errors.UnexpectedSmartServerResponse(response)
2777
self._write_group_tokens = [token.decode('utf-8') for token in response[1:]]
1708
2779
def has_signature_for_revision_id(self, revision_id):
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2780
path = self.controldir._path_for_remote_call(self._client)
2782
response = self._call(b'Repository.has_signature_for_revision_id',
2784
except errors.UnknownSmartMethod:
2786
return self._real_repository.has_signature_for_revision_id(
2788
if response[0] not in (b'yes', b'no'):
2789
raise SmartProtocolError('unexpected response code %s' % (response,))
2790
if response[0] == b'yes':
2792
for fallback in self._fallback_repositories:
2793
if fallback.has_signature_for_revision_id(revision_id):
2797
def verify_revision_signature(self, revision_id, gpg_strategy):
2798
with self.lock_read():
2799
if not self.has_signature_for_revision_id(revision_id):
2800
return gpg.SIGNATURE_NOT_SIGNED, None
2801
signature = self.get_signature_text(revision_id)
2803
testament = _mod_testament.Testament.from_revision(self, revision_id)
2805
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2806
if testament.as_short_text() != signed_plaintext:
2807
return gpg.SIGNATURE_NOT_VALID, None
2808
return (status, key)
1712
2810
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
1713
2811
self._ensure_real()
1714
2812
return self._real_repository.item_keys_introduced_by(revision_ids,
1715
2813
_files_pb=_files_pb)
1717
def revision_graph_can_have_wrong_parents(self):
1718
# The answer depends on the remote repo format.
1720
return self._real_repository.revision_graph_can_have_wrong_parents()
1722
2815
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
1723
2816
self._ensure_real()
1724
2817
return self._real_repository._find_inconsistent_revision_parents(
1742
2834
:param recipe: A search recipe (start, stop, count).
1743
2835
:return: Serialised bytes.
1745
start_keys = ' '.join(recipe[1])
1746
stop_keys = ' '.join(recipe[2])
1747
count = str(recipe[3])
1748
return '\n'.join((start_keys, stop_keys, count))
2837
start_keys = b' '.join(recipe[1])
2838
stop_keys = b' '.join(recipe[2])
2839
count = str(recipe[3]).encode('ascii')
2840
return b'\n'.join((start_keys, stop_keys, count))
1750
2842
def _serialise_search_result(self, search_result):
1751
if isinstance(search_result, graph.PendingAncestryResult):
1752
parts = ['ancestry-of']
1753
parts.extend(search_result.heads)
1755
recipe = search_result.get_recipe()
1756
parts = [recipe[0], self._serialise_search_recipe(recipe)]
1757
return '\n'.join(parts)
2843
parts = search_result.get_network_struct()
2844
return b'\n'.join(parts)
1759
2846
def autopack(self):
1760
path = self.bzrdir._path_for_remote_call(self._client)
2847
path = self.controldir._path_for_remote_call(self._client)
1762
response = self._call('PackRepository.autopack', path)
2849
response = self._call(b'PackRepository.autopack', path)
1763
2850
except errors.UnknownSmartMethod:
1764
2851
self._ensure_real()
1765
2852
self._real_repository._pack_collection.autopack()
1767
2854
self.refresh_data()
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2855
if response[0] != b'ok':
2856
raise errors.UnexpectedSmartServerResponse(response)
2858
def _revision_archive(self, revision_id, format, name, root, subdir,
2860
path = self.controldir._path_for_remote_call(self._client)
2861
format = format or ''
2863
subdir = subdir or ''
2864
force_mtime = int(force_mtime) if force_mtime is not None else None
2866
response, protocol = self._call_expecting_body(
2867
b'Repository.revision_archive', path,
2869
format.encode('ascii'),
2870
os.path.basename(name).encode('utf-8'),
2871
root.encode('utf-8'),
2872
subdir.encode('utf-8'),
2874
except errors.UnknownSmartMethod:
2876
if response[0] == b'ok':
2877
return iter([protocol.read_body_bytes()])
2878
raise errors.UnexpectedSmartServerResponse(response)
2880
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2881
path = self.controldir._path_for_remote_call(self._client)
2882
tree_path = tree_path.encode('utf-8')
2883
file_id = file_id or b''
2884
default_revision = default_revision or b''
2886
response, handler = self._call_expecting_body(
2887
b'Repository.annotate_file_revision', path,
2888
revid, tree_path, file_id, default_revision)
2889
except errors.UnknownSmartMethod:
2891
if response[0] != b'ok':
2892
raise errors.UnexpectedSmartServerResponse(response)
2893
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2896
class RemoteStreamSink(vf_repository.StreamSink):
1774
2898
def _insert_real(self, stream, src_format, resume_tokens):
1775
2899
self.target_repo._ensure_real()
2059
3239
def network_name(self):
2060
3240
return self._network_name
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
3242
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3243
return a_controldir.open_branch(name=name,
2064
3244
ignore_fallbacks=ignore_fallbacks)
2066
def _vfs_initialize(self, a_bzrdir, name):
3246
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2067
3248
# Initialisation when using a local bzrdir object, or a non-vfs init
2068
3249
# method is not available on the server.
2069
3250
# self._custom_format is always set - the start of initialize ensures
2071
if isinstance(a_bzrdir, RemoteBzrDir):
2072
a_bzrdir._ensure_real()
2073
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3252
if isinstance(a_controldir, RemoteBzrDir):
3253
a_controldir._ensure_real()
3254
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3255
name=name, append_revisions_only=append_revisions_only,
3256
repository=repository)
2076
3258
# We assume the bzrdir is parameterised; it may not be.
2077
result = self._custom_format.initialize(a_bzrdir, name)
2078
if (isinstance(a_bzrdir, RemoteBzrDir) and
3259
result = self._custom_format.initialize(a_controldir, name=name,
3260
append_revisions_only=append_revisions_only,
3261
repository=repository)
3262
if (isinstance(a_controldir, RemoteBzrDir) and
2079
3263
not isinstance(result, RemoteBranch)):
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3264
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2084
def initialize(self, a_bzrdir, name=None):
3268
def initialize(self, a_controldir, name=None, repository=None,
3269
append_revisions_only=None):
3271
name = a_controldir._get_selected_branch()
2085
3272
# 1) get the network name to use.
2086
3273
if self._custom_format:
2087
3274
network_name = self._custom_format.network_name()
2089
# Select the current bzrlib default and ask for that.
2090
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3276
# Select the current breezy default and ask for that.
3277
reference_bzrdir_format = controldir.format_registry.get('default')()
2091
3278
reference_format = reference_bzrdir_format.get_branch_format()
2092
3279
self._custom_format = reference_format
2093
3280
network_name = reference_format.network_name()
2094
3281
# Being asked to create on a non RemoteBzrDir:
2095
if not isinstance(a_bzrdir, RemoteBzrDir):
2096
return self._vfs_initialize(a_bzrdir, name=name)
2097
medium = a_bzrdir._client._medium
3282
if not isinstance(a_controldir, RemoteBzrDir):
3283
return self._vfs_initialize(a_controldir, name=name,
3284
append_revisions_only=append_revisions_only,
3285
repository=repository)
3286
medium = a_controldir._client._medium
2098
3287
if medium._is_remote_before((1, 13)):
2099
return self._vfs_initialize(a_bzrdir, name=name)
3288
return self._vfs_initialize(a_controldir, name=name,
3289
append_revisions_only=append_revisions_only,
3290
repository=repository)
2100
3291
# Creating on a remote bzr dir.
2101
3292
# 2) try direct creation via RPC
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3293
path = a_controldir._path_for_remote_call(a_controldir._client)
2104
3295
# XXX JRV20100304: Support creating colocated branches
2105
3296
raise errors.NoColocatedBranchSupport(self)
2106
verb = 'BzrDir.create_branch'
3297
verb = b'BzrDir.create_branch'
2108
response = a_bzrdir._call(verb, path, network_name)
3299
response = a_controldir._call(verb, path, network_name)
2109
3300
except errors.UnknownSmartMethod:
2110
3301
# Fallback - use vfs methods
2111
3302
medium._remember_remote_is_before((1, 13))
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3303
return self._vfs_initialize(a_controldir, name=name,
3304
append_revisions_only=append_revisions_only,
3305
repository=repository)
3306
if response[0] != b'ok':
2114
3307
raise errors.UnexpectedSmartServerResponse(response)
2115
3308
# Turn the response into a RemoteRepository object.
2116
3309
format = RemoteBranchFormat(network_name=response[1])
2117
3310
repo_format = response_tuple_to_repo_format(response[3:])
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3311
repo_path = response[2].decode('utf-8')
3312
if repository is not None:
3313
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3314
url_diff = urlutils.relative_url(repository.user_url,
3317
raise AssertionError(
3318
'repository.user_url %r does not match URL from server '
3319
'response (%r + %r)'
3320
% (repository.user_url, a_controldir.user_url, repo_path))
3321
remote_repo = repository
2121
repo_bzrdir = RemoteBzrDir(
2122
a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
2124
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2125
remote_branch = RemoteBranch(a_bzrdir, remote_repo,
3324
repo_bzrdir = a_controldir
3326
repo_bzrdir = RemoteBzrDir(
3327
a_controldir.root_transport.clone(repo_path), a_controldir._format,
3328
a_controldir._client)
3329
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3330
remote_branch = RemoteBranch(a_controldir, remote_repo,
2126
3331
format=format, setup_stacking=False, name=name)
3332
if append_revisions_only:
3333
remote_branch.set_append_revisions_only(append_revisions_only)
2127
3334
# XXX: We know this is a new branch, so it must have revno 0, revid
2128
3335
# NULL_REVISION. Creating the branch locked would make this be unable
2129
3336
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
2148
3355
self._ensure_real()
2149
3356
return self._custom_format.supports_set_append_revisions_only()
3358
def _use_default_local_heads_to_fetch(self):
3359
# If the branch format is a metadir format *and* its heads_to_fetch
3360
# implementation is not overridden vs the base class, we can use the
3361
# base class logic rather than use the heads_to_fetch RPC. This is
3362
# usually cheaper in terms of net round trips, as the last-revision and
3363
# tags info fetched is cached and would be fetched anyway.
3365
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3366
branch_class = self._custom_format._branch_class()
3367
heads_to_fetch_impl = get_unbound_function(branch_class.heads_to_fetch)
3368
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3373
class RemoteBranchStore(_mod_config.IniFileStore):
3374
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3376
Note that this is specific to bzr-based formats.
3379
def __init__(self, branch):
3380
super(RemoteBranchStore, self).__init__()
3381
self.branch = branch
3383
self._real_store = None
3385
def external_url(self):
3386
return urlutils.join(self.branch.user_url, 'branch.conf')
3388
def _load_content(self):
3389
path = self.branch._remote_path()
3391
response, handler = self.branch._call_expecting_body(
3392
b'Branch.get_config_file', path)
3393
except errors.UnknownSmartMethod:
3395
return self._real_store._load_content()
3396
if len(response) and response[0] != b'ok':
3397
raise errors.UnexpectedSmartServerResponse(response)
3398
return handler.read_body_bytes()
3400
def _save_content(self, content):
3401
path = self.branch._remote_path()
3403
response, handler = self.branch._call_with_body_bytes_expecting_body(
3404
b'Branch.put_config_file', (path,
3405
self.branch._lock_token, self.branch._repo_lock_token),
3407
except errors.UnknownSmartMethod:
3409
return self._real_store._save_content(content)
3410
handler.cancel_read_body()
3411
if response != (b'ok', ):
3412
raise errors.UnexpectedSmartServerResponse(response)
3414
def _ensure_real(self):
3415
self.branch._ensure_real()
3416
if self._real_store is None:
3417
self._real_store = _mod_config.BranchStore(self.branch)
2152
3420
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2153
3421
"""Branch stored on a server accessed by HPSS RPC.
2644
3964
self._ensure_real()
2645
3965
return self._real_branch._set_parent_location(url)
2648
3967
def pull(self, source, overwrite=False, stop_revision=None,
2650
self._clear_cached_state_of_remote_branch_only()
2652
return self._real_branch.pull(
2653
source, overwrite=overwrite, stop_revision=stop_revision,
2654
_override_hook_target=self, **kwargs)
2657
def push(self, target, overwrite=False, stop_revision=None):
2659
return self._real_branch.push(
2660
target, overwrite=overwrite, stop_revision=stop_revision,
2661
_override_hook_source_branch=self)
3969
with self.lock_write():
3970
self._clear_cached_state_of_remote_branch_only()
3972
return self._real_branch.pull(
3973
source, overwrite=overwrite, stop_revision=stop_revision,
3974
_override_hook_target=self, **kwargs)
3976
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
3977
with self.lock_read():
3979
return self._real_branch.push(
3980
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
3981
_override_hook_source_branch=self)
3983
def peek_lock_mode(self):
3984
return self._lock_mode
2663
3986
def is_locked(self):
2664
3987
return self._lock_count >= 1
3989
def revision_id_to_dotted_revno(self, revision_id):
3990
"""Given a revision id, return its dotted revno.
3992
:return: a tuple like (1,) or (400,1,3).
3994
with self.lock_read():
3996
response = self._call(b'Branch.revision_id_to_revno',
3997
self._remote_path(), revision_id)
3998
except errors.UnknownSmartMethod:
4000
return self._real_branch.revision_id_to_dotted_revno(revision_id)
4001
if response[0] == b'ok':
4002
return tuple([int(x) for x in response[1:]])
4004
raise errors.UnexpectedSmartServerResponse(response)
2667
4006
def revision_id_to_revno(self, revision_id):
2669
return self._real_branch.revision_id_to_revno(revision_id)
4007
"""Given a revision id on the branch mainline, return its revno.
4011
with self.lock_read():
4013
response = self._call(b'Branch.revision_id_to_revno',
4014
self._remote_path(), revision_id)
4015
except errors.UnknownSmartMethod:
4017
return self._real_branch.revision_id_to_revno(revision_id)
4018
if response[0] == b'ok':
4019
if len(response) == 2:
4020
return int(response[1])
4021
raise NoSuchRevision(self, revision_id)
4023
raise errors.UnexpectedSmartServerResponse(response)
2672
4025
def set_last_revision_info(self, revno, revision_id):
2673
# XXX: These should be returned by the set_last_revision_info verb
2674
old_revno, old_revid = self.last_revision_info()
2675
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2676
revision_id = ensure_null(revision_id)
2678
response = self._call('Branch.set_last_revision_info',
2679
self._remote_path(), self._lock_token, self._repo_lock_token,
2680
str(revno), revision_id)
2681
except errors.UnknownSmartMethod:
2683
self._clear_cached_state_of_remote_branch_only()
2684
self._real_branch.set_last_revision_info(revno, revision_id)
2685
self._last_revision_info_cache = revno, revision_id
2687
if response == ('ok',):
2688
self._clear_cached_state()
2689
self._last_revision_info_cache = revno, revision_id
2690
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2691
# Update the _real_branch's cache too.
2692
if self._real_branch is not None:
2693
cache = self._last_revision_info_cache
2694
self._real_branch._last_revision_info_cache = cache
2696
raise errors.UnexpectedSmartServerResponse(response)
4026
with self.lock_write():
4027
# XXX: These should be returned by the set_last_revision_info verb
4028
old_revno, old_revid = self.last_revision_info()
4029
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4030
if not revision_id or not isinstance(revision_id, bytes):
4031
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
4033
response = self._call(b'Branch.set_last_revision_info',
4034
self._remote_path(), self._lock_token, self._repo_lock_token,
4035
str(revno).encode('ascii'), revision_id)
4036
except errors.UnknownSmartMethod:
4038
self._clear_cached_state_of_remote_branch_only()
4039
self._real_branch.set_last_revision_info(revno, revision_id)
4040
self._last_revision_info_cache = revno, revision_id
4042
if response == (b'ok',):
4043
self._clear_cached_state()
4044
self._last_revision_info_cache = revno, revision_id
4045
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4046
# Update the _real_branch's cache too.
4047
if self._real_branch is not None:
4048
cache = self._last_revision_info_cache
4049
self._real_branch._last_revision_info_cache = cache
4051
raise errors.UnexpectedSmartServerResponse(response)
2699
4053
def generate_revision_history(self, revision_id, last_rev=None,
2700
4054
other_branch=None):
2701
medium = self._client._medium
2702
if not medium._is_remote_before((1, 6)):
2703
# Use a smart method for 1.6 and above servers
2705
self._set_last_revision_descendant(revision_id, other_branch,
2706
allow_diverged=True, allow_overwrite_descendant=True)
2708
except errors.UnknownSmartMethod:
2709
medium._remember_remote_is_before((1, 6))
2710
self._clear_cached_state_of_remote_branch_only()
2711
self.set_revision_history(self._lefthand_history(revision_id,
2712
last_rev=last_rev,other_branch=other_branch))
4055
with self.lock_write():
4056
medium = self._client._medium
4057
if not medium._is_remote_before((1, 6)):
4058
# Use a smart method for 1.6 and above servers
4060
self._set_last_revision_descendant(revision_id, other_branch,
4061
allow_diverged=True, allow_overwrite_descendant=True)
4063
except errors.UnknownSmartMethod:
4064
medium._remember_remote_is_before((1, 6))
4065
self._clear_cached_state_of_remote_branch_only()
4066
graph = self.repository.get_graph()
4067
(last_revno, last_revid) = self.last_revision_info()
4068
known_revision_ids = [
4069
(last_revid, last_revno),
4070
(_mod_revision.NULL_REVISION, 0),
4072
if last_rev is not None:
4073
if not graph.is_ancestor(last_rev, revision_id):
4074
# our previous tip is not merged into stop_revision
4075
raise errors.DivergedBranches(self, other_branch)
4076
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
4077
self.set_last_revision_info(revno, revision_id)
2714
4079
def set_push_location(self, location):
4080
self._set_config_location('push_location', location)
4082
def heads_to_fetch(self):
4083
if self._format._use_default_local_heads_to_fetch():
4084
# We recognise this format, and its heads-to-fetch implementation
4085
# is the default one (tip + tags). In this case it's cheaper to
4086
# just use the default implementation rather than a special RPC as
4087
# the tip and tags data is cached.
4088
return branch.Branch.heads_to_fetch(self)
4089
medium = self._client._medium
4090
if medium._is_remote_before((2, 4)):
4091
return self._vfs_heads_to_fetch()
4093
return self._rpc_heads_to_fetch()
4094
except errors.UnknownSmartMethod:
4095
medium._remember_remote_is_before((2, 4))
4096
return self._vfs_heads_to_fetch()
4098
def _rpc_heads_to_fetch(self):
4099
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4100
if len(response) != 2:
4101
raise errors.UnexpectedSmartServerResponse(response)
4102
must_fetch, if_present_fetch = response
4103
return set(must_fetch), set(if_present_fetch)
4105
def _vfs_heads_to_fetch(self):
2715
4106
self._ensure_real()
2716
return self._real_branch.set_push_location(location)
4107
return self._real_branch.heads_to_fetch()
2719
4110
class RemoteConfig(object):
2774
4175
medium = self._branch._client._medium
2775
4176
if medium._is_remote_before((1, 14)):
2776
4177
return self._vfs_set_option(value, name, section)
4178
if isinstance(value, dict):
4179
if medium._is_remote_before((2, 2)):
4180
return self._vfs_set_option(value, name, section)
4181
return self._set_config_option_dict(value, name, section)
4183
return self._set_config_option(value, name, section)
4185
def _set_config_option(self, value, name, section):
2778
4187
path = self._branch._remote_path()
2779
response = self._branch._client.call('Branch.set_config_option',
4188
response = self._branch._client.call(b'Branch.set_config_option',
2780
4189
path, self._branch._lock_token, self._branch._repo_lock_token,
2781
value.encode('utf8'), name, section or '')
4190
value.encode('utf8'), name.encode('utf-8'),
4191
(section or '').encode('utf-8'))
2782
4192
except errors.UnknownSmartMethod:
4193
medium = self._branch._client._medium
2783
4194
medium._remember_remote_is_before((1, 14))
2784
4195
return self._vfs_set_option(value, name, section)
2785
4196
if response != ():
2786
4197
raise errors.UnexpectedSmartServerResponse(response)
4199
def _serialize_option_dict(self, option_dict):
4201
for key, value in option_dict.items():
4202
if isinstance(key, text_type):
4203
key = key.encode('utf8')
4204
if isinstance(value, text_type):
4205
value = value.encode('utf8')
4206
utf8_dict[key] = value
4207
return bencode.bencode(utf8_dict)
4209
def _set_config_option_dict(self, value, name, section):
4211
path = self._branch._remote_path()
4212
serialised_dict = self._serialize_option_dict(value)
4213
response = self._branch._client.call(
4214
b'Branch.set_config_option_dict',
4215
path, self._branch._lock_token, self._branch._repo_lock_token,
4216
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4217
except errors.UnknownSmartMethod:
4218
medium = self._branch._client._medium
4219
medium._remember_remote_is_before((2, 2))
4220
return self._vfs_set_option(value, name, section)
4222
raise errors.UnexpectedSmartServerResponse(response)
2788
4224
def _real_object(self):
2789
4225
self._branch._ensure_real()
2790
4226
return self._branch._real_branch
2867
4297
return context['path']
2868
except KeyError, key_err:
2870
return err.error_args[0]
2871
except IndexError, idx_err:
2873
'Missing key %r in context %r', key_err.args[0], context)
4300
return err.error_args[0].decode('utf-8')
4302
mutter('Missing key \'path\' in context %r', context)
2876
if err.error_verb == 'IncompatibleRepositories':
2877
raise errors.IncompatibleRepositories(err.error_args[0],
2878
err.error_args[1], err.error_args[2])
2879
elif err.error_verb == 'NoSuchRevision':
2880
raise NoSuchRevision(find('branch'), err.error_args[0])
2881
elif err.error_verb == 'nosuchrevision':
2882
raise NoSuchRevision(find('repository'), err.error_args[0])
2883
elif err.error_verb == 'nobranch':
2884
if len(err.error_args) >= 1:
2885
extra = err.error_args[0]
2888
raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
2890
elif err.error_verb == 'norepository':
2891
raise errors.NoRepositoryPresent(find('bzrdir'))
2892
elif err.error_verb == 'LockContention':
2893
raise errors.LockContention('(remote lock)')
2894
elif err.error_verb == 'UnlockableTransport':
2895
raise errors.UnlockableTransport(find('bzrdir').root_transport)
2896
elif err.error_verb == 'LockFailed':
2897
raise errors.LockFailed(err.error_args[0], err.error_args[1])
2898
elif err.error_verb == 'TokenMismatch':
2899
raise errors.TokenMismatch(find('token'), '(remote token)')
2900
elif err.error_verb == 'Diverged':
2901
raise errors.DivergedBranches(find('branch'), find('other_branch'))
2902
elif err.error_verb == 'TipChangeRejected':
2903
raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
2904
elif err.error_verb == 'UnstackableBranchFormat':
2905
raise errors.UnstackableBranchFormat(*err.error_args)
2906
elif err.error_verb == 'UnstackableRepositoryFormat':
2907
raise errors.UnstackableRepositoryFormat(*err.error_args)
2908
elif err.error_verb == 'NotStacked':
2909
raise errors.NotStacked(branch=find('branch'))
2910
elif err.error_verb == 'PermissionDenied':
2912
if len(err.error_args) >= 2:
2913
extra = err.error_args[1]
2916
raise errors.PermissionDenied(path, extra=extra)
2917
elif err.error_verb == 'ReadError':
2919
raise errors.ReadError(path)
2920
elif err.error_verb == 'NoSuchFile':
2922
raise errors.NoSuchFile(path)
2923
elif err.error_verb == 'FileExists':
2924
raise errors.FileExists(err.error_args[0])
2925
elif err.error_verb == 'DirectoryNotEmpty':
2926
raise errors.DirectoryNotEmpty(err.error_args[0])
2927
elif err.error_verb == 'ShortReadvError':
2928
args = err.error_args
2929
raise errors.ShortReadvError(
2930
args[0], int(args[1]), int(args[2]), int(args[3]))
2931
elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
2932
encoding = str(err.error_args[0]) # encoding must always be a string
2933
val = err.error_args[1]
2934
start = int(err.error_args[2])
2935
end = int(err.error_args[3])
2936
reason = str(err.error_args[4]) # reason must always be a string
4304
if not isinstance(err.error_verb, bytes):
4305
raise TypeError(err.error_verb)
4307
translator = error_translators.get(err.error_verb)
4311
raise translator(err, find, get_path)
4313
translator = no_context_error_translators.get(err.error_verb)
4315
raise errors.UnknownErrorFromSmartServer(err)
4317
raise translator(err)
4320
error_translators.register(b'NoSuchRevision',
4321
lambda err, find, get_path: NoSuchRevision(
4322
find('branch'), err.error_args[0]))
4323
error_translators.register(b'nosuchrevision',
4324
lambda err, find, get_path: NoSuchRevision(
4325
find('repository'), err.error_args[0]))
4327
def _translate_nobranch_error(err, find, get_path):
4328
if len(err.error_args) >= 1:
4329
extra = err.error_args[0].decode('utf-8')
4332
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4335
error_translators.register(b'nobranch', _translate_nobranch_error)
4336
error_translators.register(b'norepository',
4337
lambda err, find, get_path: errors.NoRepositoryPresent(
4339
error_translators.register(b'UnlockableTransport',
4340
lambda err, find, get_path: errors.UnlockableTransport(
4341
find('bzrdir').root_transport))
4342
error_translators.register(b'TokenMismatch',
4343
lambda err, find, get_path: errors.TokenMismatch(
4344
find('token'), '(remote token)'))
4345
error_translators.register(b'Diverged',
4346
lambda err, find, get_path: errors.DivergedBranches(
4347
find('branch'), find('other_branch')))
4348
error_translators.register(b'NotStacked',
4349
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4351
def _translate_PermissionDenied(err, find, get_path):
4353
if len(err.error_args) >= 2:
4354
extra = err.error_args[1].decode('utf-8')
4357
return errors.PermissionDenied(path, extra=extra)
4359
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4360
error_translators.register(b'ReadError',
4361
lambda err, find, get_path: errors.ReadError(get_path()))
4362
error_translators.register(b'NoSuchFile',
4363
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4364
error_translators.register(b'TokenLockingNotSupported',
4365
lambda err, find, get_path: errors.TokenLockingNotSupported(
4366
find('repository')))
4367
error_translators.register(b'UnsuspendableWriteGroup',
4368
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4369
repository=find('repository')))
4370
error_translators.register(b'UnresumableWriteGroup',
4371
lambda err, find, get_path: errors.UnresumableWriteGroup(
4372
repository=find('repository'), write_groups=err.error_args[0],
4373
reason=err.error_args[1]))
4374
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4375
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4376
no_context_error_translators.register(b'IncompatibleRepositories',
4377
lambda err: errors.IncompatibleRepositories(
4378
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4379
no_context_error_translators.register(b'LockContention',
4380
lambda err: errors.LockContention('(remote lock)'))
4381
no_context_error_translators.register(b'LockFailed',
4382
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4383
no_context_error_translators.register(b'TipChangeRejected',
4384
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4385
no_context_error_translators.register(b'UnstackableBranchFormat',
4386
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4387
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4388
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4389
no_context_error_translators.register(b'FileExists',
4390
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4391
no_context_error_translators.register(b'DirectoryNotEmpty',
4392
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4393
no_context_error_translators.register(b'UnknownFormat',
4394
lambda err: errors.UnknownFormatError(
4395
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4396
no_context_error_translators.register(b'InvalidURL',
4397
lambda err: urlutils.InvalidURL(
4398
err.error_args[0].decode('utf-8'), err.error_args[1].decode('ascii')))
4400
def _translate_short_readv_error(err):
4401
args = err.error_args
4402
return errors.ShortReadvError(
4403
args[0].decode('utf-8'),
4404
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4405
int(args[3].decode('ascii')))
4407
no_context_error_translators.register(b'ShortReadvError',
4408
_translate_short_readv_error)
4410
def _translate_unicode_error(err):
4411
encoding = err.error_args[0].decode('ascii')
4412
val = err.error_args[1].decode('utf-8')
4413
start = int(err.error_args[2].decode('ascii'))
4414
end = int(err.error_args[3].decode('ascii'))
4415
reason = err.error_args[4].decode('utf-8')
2937
4416
if val.startswith('u:'):
2938
4417
val = val[2:].decode('utf-8')
2939
4418
elif val.startswith('s:'):