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)
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'.':
296
repo_bzrdir_format = RemoteBzrDirFormat()
297
repo_bzrdir_format._network_name = response[5]
298
repo_bzr = RemoteBzrDir(transport.clone(repo_path),
302
final_stack = response[8] or None
303
final_stack_pwd = response[9] or None
305
final_stack_pwd = urlutils.join(
306
transport.base, final_stack_pwd)
307
remote_repo = RemoteRepository(repo_bzr, repo_format)
308
if len(response) > 10:
309
# Updated server verb that locks remotely.
310
repo_lock_token = response[10] or None
311
remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
313
remote_repo.dont_leave_lock_in_place()
315
remote_repo.lock_write()
316
policy = _mod_bzrdir.UseExistingRepository(remote_repo,
317
final_stack, final_stack_pwd, require_stacking)
318
policy.acquire_repository()
322
bzrdir._format.set_branch_format(self.get_branch_format())
324
# The repo has already been created, but we need to make sure that
325
# we'll make a stackable branch.
326
bzrdir._format.require_stacking(_skip_repo=True)
327
return remote_repo, bzrdir, require_stacking, policy
329
def _open(self, transport):
330
return RemoteBzrDir(transport, self)
332
def __eq__(self, other):
333
if not isinstance(other, RemoteBzrDirFormat):
335
return self.get_format_description() == other.get_format_description()
337
def __return_repository_format(self):
338
# Always return a RemoteRepositoryFormat object, but if a specific bzr
339
# repository format has been asked for, tell the RemoteRepositoryFormat
340
# that it should use that for init() etc.
341
result = RemoteRepositoryFormat()
342
custom_format = getattr(self, '_repository_format', None)
344
if isinstance(custom_format, RemoteRepositoryFormat):
347
# We will use the custom format to create repositories over the
348
# wire; expose its details like rich_root_data for code to
350
result._custom_format = custom_format
353
def get_branch_format(self):
354
result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
355
if not isinstance(result, RemoteBranchFormat):
356
new_result = RemoteBranchFormat()
357
new_result._custom_format = result
359
self.set_branch_format(new_result)
363
repository_format = property(__return_repository_format,
364
_mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
367
class RemoteControlStore(_mod_config.IniFileStore):
368
"""Control store which attempts to use HPSS calls to retrieve control store.
370
Note that this is specific to bzr-based formats.
373
def __init__(self, bzrdir):
374
super(RemoteControlStore, self).__init__()
375
self.controldir = bzrdir
376
self._real_store = None
378
def lock_write(self, token=None):
380
return self._real_store.lock_write(token)
384
return self._real_store.unlock()
387
with self.lock_write():
388
# We need to be able to override the undecorated implementation
389
self.save_without_locking()
391
def save_without_locking(self):
392
super(RemoteControlStore, self).save()
394
def _ensure_real(self):
395
self.controldir._ensure_real()
396
if self._real_store is None:
397
self._real_store = _mod_config.ControlStore(self.controldir)
399
def external_url(self):
400
return urlutils.join(self.branch.user_url, 'control.conf')
402
def _load_content(self):
403
medium = self.controldir._client._medium
404
path = self.controldir._path_for_remote_call(self.controldir._client)
406
response, handler = self.controldir._call_expecting_body(
407
b'BzrDir.get_config_file', path)
408
except errors.UnknownSmartMethod:
410
return self._real_store._load_content()
411
if len(response) and response[0] != b'ok':
412
raise errors.UnexpectedSmartServerResponse(response)
413
return handler.read_body_bytes()
415
def _save_content(self, content):
416
# FIXME JRV 2011-11-22: Ideally this should use a
417
# HPSS call too, but at the moment it is not possible
418
# to write lock control directories.
420
return self._real_store._save_content(content)
423
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
89
# Note: RemoteBzrDirFormat is in bzrdir.py
91
class RemoteBzrDir(BzrDir, _RpcHelper):
424
92
"""Control directory on a remote server, accessed via bzr:// or similar."""
426
94
def __init__(self, transport, format, _client=None, _force_probe=False):
560
186
medium = self._client._medium
561
187
if medium._is_remote_before((1, 13)):
562
188
return self._vfs_cloning_metadir(require_stacking=require_stacking)
563
verb = b'BzrDir.cloning_metadir'
189
verb = 'BzrDir.cloning_metadir'
564
190
if require_stacking:
568
194
path = self._path_for_remote_call(self._client)
570
196
response = self._call(verb, path, stacking)
571
197
except errors.UnknownSmartMethod:
572
198
medium._remember_remote_is_before((1, 13))
573
199
return self._vfs_cloning_metadir(require_stacking=require_stacking)
574
except errors.UnknownErrorFromSmartServer as err:
575
if err.error_tuple != (b'BranchReference',):
200
except errors.UnknownErrorFromSmartServer, err:
201
if err.error_tuple != ('BranchReference',):
577
203
# We need to resolve the branch reference to determine the
578
204
# cloning_metadir. This causes unnecessary RPCs to open the
579
205
# referenced branch (and bzrdir, etc) but only when the caller
580
206
# didn't already resolve the branch reference.
581
207
referenced_branch = self.open_branch()
582
return referenced_branch.controldir.cloning_metadir()
208
return referenced_branch.bzrdir.cloning_metadir()
583
209
if len(response) != 3:
584
210
raise errors.UnexpectedSmartServerResponse(response)
585
211
control_name, repo_name, branch_info = response
586
212
if len(branch_info) != 2:
587
213
raise errors.UnexpectedSmartServerResponse(response)
588
214
branch_ref, branch_name = branch_info
590
format = controldir.network_format_registry.get(control_name)
592
raise errors.UnknownFormatError(kind='control', format=control_name)
215
format = bzrdir.network_format_registry.get(control_name)
596
format.repository_format = _mod_repository.network_format_registry.get(
599
raise errors.UnknownFormatError(kind='repository',
601
if branch_ref == b'ref':
217
format.repository_format = repository.network_format_registry.get(
219
if branch_ref == 'ref':
602
220
# XXX: we need possible_transports here to avoid reopening the
603
221
# connection to the referenced location
604
ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
222
ref_bzrdir = BzrDir.open(branch_name)
605
223
branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
606
224
format.set_branch_format(branch_format)
607
elif branch_ref == b'branch':
225
elif branch_ref == 'branch':
610
branch_format = branch.network_format_registry.get(
613
raise errors.UnknownFormatError(kind='branch',
615
format.set_branch_format(branch_format)
227
format.set_branch_format(
228
branch.network_format_registry.get(branch_name))
617
230
raise errors.UnexpectedSmartServerResponse(response)
669
266
def destroy_branch(self, name=None):
670
267
"""See BzrDir.destroy_branch"""
672
name = self._get_selected_branch()
674
raise errors.NoColocatedBranchSupport(self)
675
path = self._path_for_remote_call(self._client)
681
response = self._call(b'BzrDir.destroy_branch', path, *args)
682
except errors.UnknownSmartMethod:
684
self._real_bzrdir.destroy_branch(name=name)
685
self._next_open_branch_result = None
269
self._real_bzrdir.destroy_branch(name=name)
687
270
self._next_open_branch_result = None
688
if response[0] != b'ok':
689
raise SmartProtocolError('unexpected response code %s' % (response,))
691
def create_workingtree(self, revision_id=None, from_branch=None,
692
accelerator_tree=None, hardlink=False):
272
def create_workingtree(self, revision_id=None, from_branch=None):
693
273
raise errors.NotLocalUrl(self.transport.base)
695
def find_branch_format(self, name=None):
275
def find_branch_format(self):
696
276
"""Find the branch 'format' for this bzrdir.
698
278
This might be a synthetic object for e.g. RemoteBranch and SVN.
700
b = self.open_branch(name=name)
280
b = self.open_branch()
703
def get_branches(self, possible_transports=None, ignore_fallbacks=False):
704
path = self._path_for_remote_call(self._client)
706
response, handler = self._call_expecting_body(
707
b'BzrDir.get_branches', path)
708
except errors.UnknownSmartMethod:
710
return self._real_bzrdir.get_branches()
711
if response[0] != b"success":
712
raise errors.UnexpectedSmartServerResponse(response)
713
body = bencode.bdecode(handler.read_body_bytes())
715
for name, value in viewitems(body):
716
ret[name] = self._open_branch(name, value[0], value[1],
717
possible_transports=possible_transports,
718
ignore_fallbacks=ignore_fallbacks)
721
def set_branch_reference(self, target_branch, name=None):
722
"""See BzrDir.set_branch_reference()."""
724
name = self._get_selected_branch()
726
raise errors.NoColocatedBranchSupport(self)
728
return self._real_bzrdir.set_branch_reference(target_branch, name=name)
730
def get_branch_reference(self, name=None):
283
def get_branch_reference(self):
731
284
"""See BzrDir.get_branch_reference()."""
733
name = self._get_selected_branch()
735
raise errors.NoColocatedBranchSupport(self)
736
285
response = self._get_branch_reference()
737
if response[0] == b'ref':
738
return response[1].decode('utf-8')
286
if response[0] == 'ref':
758
307
medium._remember_remote_is_before(required_version)
761
if verb == b'BzrDir.open_branch':
762
if response[0] != b'ok':
310
if verb == 'BzrDir.open_branch':
311
if response[0] != 'ok':
763
312
raise errors.UnexpectedSmartServerResponse(response)
764
313
if response[1] != '':
765
return (b'ref', response[1])
314
return ('ref', response[1])
767
return (b'branch', '')
768
if response[0] not in (b'ref', b'branch'):
316
return ('branch', '')
317
if response[0] not in ('ref', 'branch'):
769
318
raise errors.UnexpectedSmartServerResponse(response)
772
def _get_tree_branch(self, name=None):
321
def _get_tree_branch(self):
773
322
"""See BzrDir._get_tree_branch()."""
774
return None, self.open_branch(name=name)
323
return None, self.open_branch()
776
def _open_branch(self, name, kind, location_or_format,
777
ignore_fallbacks=False, possible_transports=None):
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':
779
336
# a branch reference, use the existing BranchReference logic.
780
337
format = BranchReferenceFormat()
781
338
return format.open(self, name=name, _found=True,
782
location=location_or_format, ignore_fallbacks=ignore_fallbacks,
783
possible_transports=possible_transports)
784
branch_format_name = location_or_format
339
location=response[1], ignore_fallbacks=ignore_fallbacks)
340
branch_format_name = response[1]
785
341
if not branch_format_name:
786
342
branch_format_name = None
787
343
format = RemoteBranchFormat(network_name=branch_format_name)
788
344
return RemoteBranch(self, self.find_repository(), format=format,
789
setup_stacking=not ignore_fallbacks, name=name,
790
possible_transports=possible_transports)
792
def open_branch(self, name=None, unsupported=False,
793
ignore_fallbacks=False, possible_transports=None):
795
name = self._get_selected_branch()
797
raise errors.NoColocatedBranchSupport(self)
799
raise NotImplementedError('unsupported flag support not implemented yet.')
800
if self._next_open_branch_result is not None:
801
# See create_branch for details.
802
result = self._next_open_branch_result
803
self._next_open_branch_result = None
805
response = self._get_branch_reference()
806
return self._open_branch(name, response[0], response[1],
807
possible_transports=possible_transports,
808
ignore_fallbacks=ignore_fallbacks)
345
setup_stacking=not ignore_fallbacks, name=name)
810
347
def _open_repo_v1(self, path):
811
verb = b'BzrDir.find_repository'
348
verb = 'BzrDir.find_repository'
812
349
response = self._call(verb, path)
813
if response[0] != b'ok':
350
if response[0] != 'ok':
814
351
raise errors.UnexpectedSmartServerResponse(response)
815
352
# servers that only support the v1 method don't support external
816
353
# references either.
817
354
self._ensure_real()
818
355
repo = self._real_bzrdir.open_repository()
819
response = response + (b'no', repo._format.network_name())
356
response = response + ('no', repo._format.network_name())
820
357
return response, repo
822
359
def _open_repo_v2(self, path):
823
verb = b'BzrDir.find_repositoryV2'
360
verb = 'BzrDir.find_repositoryV2'
824
361
response = self._call(verb, path)
825
if response[0] != b'ok':
362
if response[0] != 'ok':
826
363
raise errors.UnexpectedSmartServerResponse(response)
827
364
self._ensure_real()
828
365
repo = self._real_bzrdir.open_repository()
919
441
"""Upgrading of remote bzrdirs is not supported yet."""
922
def needs_format_conversion(self, format):
444
def needs_format_conversion(self, format=None):
923
445
"""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)
926
457
def _get_config(self):
927
458
return RemoteBzrDirConfig(self)
929
def _get_config_store(self):
930
return RemoteControlStore(self)
933
class RemoteInventoryTree(InventoryRevisionTree):
935
def __init__(self, repository, inv, revision_id):
936
super(RemoteInventoryTree, self).__init__(repository, inv, revision_id)
938
def archive(self, format, name, root=None, subdir=None, force_mtime=None):
939
ret = self._repository._revision_archive(
940
self.get_revision_id(), format, name, root, subdir,
941
force_mtime=force_mtime)
943
return super(RemoteInventoryTree, self).archive(
944
format, name, root, subdir, force_mtime=force_mtime)
947
def annotate_iter(self, path, file_id=None,
948
default_revision=_mod_revision.CURRENT_REVISION):
949
"""Return an iterator of revision_id, line tuples.
951
For working trees (and mutable trees in general), the special
952
revision_id 'current:' will be used for lines that are new in this
953
tree, e.g. uncommitted changes.
954
:param file_id: The file to produce an annotated version from
955
:param default_revision: For lines that don't match a basis, mark them
956
with this revision id. Not all implementations will make use of
959
ret = self._repository._annotate_file_revision(
960
self.get_revision_id(), path, file_id, default_revision)
962
return super(RemoteInventoryTree, self).annotate_iter(
963
path, file_id, default_revision=default_revision)
967
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
968
462
"""Format for repositories accessed over a _SmartClient.
970
464
Instances of this repository are represented by RemoteRepository
1057
529
self._custom_format.supports_tree_reference
1058
530
return self._supports_tree_reference
1061
def revision_graph_can_have_wrong_parents(self):
1062
if self._revision_graph_can_have_wrong_parents is None:
1064
self._revision_graph_can_have_wrong_parents = \
1065
self._custom_format.revision_graph_can_have_wrong_parents
1066
return self._revision_graph_can_have_wrong_parents
1068
def _vfs_initialize(self, a_controldir, shared):
532
def _vfs_initialize(self, a_bzrdir, shared):
1069
533
"""Helper for common code in initialize."""
1070
534
if self._custom_format:
1071
535
# Custom format requested
1072
result = self._custom_format.initialize(a_controldir, shared=shared)
536
result = self._custom_format.initialize(a_bzrdir, shared=shared)
1073
537
elif self._creating_bzrdir is not None:
1074
538
# Use the format that the repository we were created to back
1076
540
prior_repo = self._creating_bzrdir.open_repository()
1077
541
prior_repo._ensure_real()
1078
542
result = prior_repo._real_repository._format.initialize(
1079
a_controldir, shared=shared)
543
a_bzrdir, shared=shared)
1081
545
# assume that a_bzr is a RemoteBzrDir but the smart server didn't
1082
546
# support remote initialization.
1083
547
# We delegate to a real object at this point (as RemoteBzrDir
1084
548
# delegate to the repository format which would lead to infinite
1085
# recursion if we just called a_controldir.create_repository.
1086
a_controldir._ensure_real()
1087
result = a_controldir._real_bzrdir.create_repository(shared=shared)
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)
1088
552
if not isinstance(result, RemoteRepository):
1089
return self.open(a_controldir)
553
return self.open(a_bzrdir)
1093
def initialize(self, a_controldir, shared=False):
557
def initialize(self, a_bzrdir, shared=False):
1094
558
# Being asked to create on a non RemoteBzrDir:
1095
if not isinstance(a_controldir, RemoteBzrDir):
1096
return self._vfs_initialize(a_controldir, shared)
1097
medium = a_controldir._client._medium
559
if not isinstance(a_bzrdir, RemoteBzrDir):
560
return self._vfs_initialize(a_bzrdir, shared)
561
medium = a_bzrdir._client._medium
1098
562
if medium._is_remote_before((1, 13)):
1099
return self._vfs_initialize(a_controldir, shared)
563
return self._vfs_initialize(a_bzrdir, shared)
1100
564
# Creating on a remote bzr dir.
1101
565
# 1) get the network name to use.
1102
566
if self._custom_format:
1104
568
elif self._network_name:
1105
569
network_name = self._network_name
1107
# Select the current breezy default and ask for that.
1108
reference_bzrdir_format = controldir.format_registry.get('default')()
571
# Select the current bzrlib default and ask for that.
572
reference_bzrdir_format = bzrdir.format_registry.get('default')()
1109
573
reference_format = reference_bzrdir_format.repository_format
1110
574
network_name = reference_format.network_name()
1111
575
# 2) try direct creation via RPC
1112
path = a_controldir._path_for_remote_call(a_controldir._client)
1113
verb = b'BzrDir.create_repository'
576
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
577
verb = 'BzrDir.create_repository'
1115
shared_str = b'True'
1117
shared_str = b'False'
1119
response = a_controldir._call(verb, path, network_name, shared_str)
583
response = a_bzrdir._call(verb, path, network_name, shared_str)
1120
584
except errors.UnknownSmartMethod:
1121
585
# Fallback - use vfs methods
1122
586
medium._remember_remote_is_before((1, 13))
1123
return self._vfs_initialize(a_controldir, shared)
587
return self._vfs_initialize(a_bzrdir, shared)
1125
589
# Turn the response into a RemoteRepository object.
1126
590
format = response_tuple_to_repo_format(response[1:])
1127
591
# Used to support creating a real format instance when needed.
1128
format._creating_bzrdir = a_controldir
1129
remote_repo = RemoteRepository(a_controldir, format)
592
format._creating_bzrdir = a_bzrdir
593
remote_repo = RemoteRepository(a_bzrdir, format)
1130
594
format._creating_repo = remote_repo
1131
595
return remote_repo
1133
def open(self, a_controldir):
1134
if not isinstance(a_controldir, RemoteBzrDir):
1135
raise AssertionError('%r is not a RemoteBzrDir' % (a_controldir,))
1136
return a_controldir.open_repository()
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()
1138
602
def _ensure_real(self):
1139
603
if self._custom_format is None:
1141
self._custom_format = _mod_repository.network_format_registry.get(
1144
raise errors.UnknownFormatError(kind='repository',
1145
format=self._network_name)
604
self._custom_format = repository.network_format_registry.get(
1148
608
def _fetch_order(self):
1497
910
# one; unfortunately the tests rely on slightly different behaviour at
1498
911
# present -- mbp 20090710
1499
912
return (self.__class__ is other.__class__ and
1500
self.controldir.transport.base == other.controldir.transport.base)
913
self.bzrdir.transport.base == other.bzrdir.transport.base)
1502
915
def get_graph(self, other_repository=None):
1503
916
"""Return the graph for this repository format"""
1504
917
parents_provider = self._make_parents_provider(other_repository)
1505
918
return graph.Graph(parents_provider)
1507
921
def get_known_graph_ancestry(self, revision_ids):
1508
922
"""Return the known graph for a set of revision ids and their ancestors.
1510
with self.lock_read():
1511
revision_graph = dict(((key, value) for key, value in
1512
self.get_graph().iter_ancestry(revision_ids) if value is not None))
1513
revision_graph = _mod_repository._strip_NULL_ghosts(revision_graph)
1514
return graph.KnownGraph(revision_graph)
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)
1516
929
def gather_stats(self, revid=None, committers=None):
1517
930
"""See Repository.gather_stats()."""
1518
path = self.controldir._path_for_remote_call(self._client)
931
path = self.bzrdir._path_for_remote_call(self._client)
1519
932
# revid can be None to indicate no revisions, not just NULL_REVISION
1520
if revid is None or _mod_revision.is_null(revid):
933
if revid is None or revision.is_null(revid):
1523
936
fmt_revid = revid
1524
937
if committers is None or not committers:
1525
fmt_committers = b'no'
938
fmt_committers = 'no'
1527
fmt_committers = b'yes'
940
fmt_committers = 'yes'
1528
941
response_tuple, response_handler = self._call_expecting_body(
1529
b'Repository.gather_stats', path, fmt_revid, fmt_committers)
1530
if response_tuple[0] != b'ok':
942
'Repository.gather_stats', path, fmt_revid, fmt_committers)
943
if response_tuple[0] != 'ok':
1531
944
raise errors.UnexpectedSmartServerResponse(response_tuple)
1533
946
body = response_handler.read_body_bytes()
1535
for line in body.split(b'\n'):
948
for line in body.split('\n'):
1538
key, val_text = line.split(b':')
1539
key = key.decode('ascii')
951
key, val_text = line.split(':')
1540
952
if key in ('revisions', 'size', 'committers'):
1541
953
result[key] = int(val_text)
1542
954
elif key in ('firstrev', 'latestrev'):
1543
values = val_text.split(b' ')[1:]
1544
result[key] = (float(values[0]), int(values[1]))
955
values = val_text.split(' ')[1:]
956
result[key] = (float(values[0]), long(values[1]))
1823
1195
raise errors.UnexpectedSmartServerResponse(response)
1825
1197
def sprout(self, to_bzrdir, revision_id=None):
1826
"""Create a descendent repository for new development.
1828
Unlike clone, this does not copy the settings of the repository.
1830
with self.lock_read():
1831
dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
1832
dest_repo.fetch(self, revision_id=revision_id)
1835
def _create_sprouting_repo(self, a_controldir, shared):
1836
if not isinstance(a_controldir._format, self.controldir._format.__class__):
1837
# use target default format.
1838
dest_repo = a_controldir.create_repository()
1840
# Most control formats need the repository to be specifically
1841
# created, but on some old all-in-one formats it's not needed
1843
dest_repo = self._format.initialize(a_controldir, shared=shared)
1844
except errors.UninitializableFormat:
1845
dest_repo = a_controldir.open_repository()
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)
1846
1203
return dest_repo
1848
1205
### These methods are just thin shims to the VFS object for now.
1850
1207
def revision_tree(self, revision_id):
1851
with self.lock_read():
1852
revision_id = _mod_revision.ensure_null(revision_id)
1853
if revision_id == _mod_revision.NULL_REVISION:
1854
return InventoryRevisionTree(self,
1855
Inventory(root_id=None), _mod_revision.NULL_REVISION)
1857
return list(self.revision_trees([revision_id]))[0]
1209
return self._real_repository.revision_tree(revision_id)
1859
1211
def get_serializer_format(self):
1860
path = self.controldir._path_for_remote_call(self._client)
1862
response = self._call(b'VersionedFileRepository.get_serializer_format',
1864
except errors.UnknownSmartMethod:
1866
return self._real_repository.get_serializer_format()
1867
if response[0] != b'ok':
1868
raise errors.UnexpectedSmartServerResponse(response)
1213
return self._real_repository.get_serializer_format()
1871
1215
def get_commit_builder(self, branch, parents, config, timestamp=None,
1872
1216
timezone=None, committer=None, revprops=None,
1873
revision_id=None, lossy=False):
1874
"""Obtain a CommitBuilder for this repository.
1876
:param branch: Branch to commit to.
1877
:param parents: Revision ids of the parents of the new revision.
1878
:param config: Configuration to use.
1879
:param timestamp: Optional timestamp recorded for commit.
1880
:param timezone: Optional timezone for timestamp.
1881
:param committer: Optional committer to set for commit.
1882
:param revprops: Optional dictionary of revision properties.
1883
:param revision_id: Optional revision id.
1884
:param lossy: Whether to discard data that can not be natively
1885
represented, when pushing to a foreign VCS
1887
if self._fallback_repositories and not self._format.supports_chks:
1888
raise errors.BzrError("Cannot commit directly to a stacked branch"
1889
" in pre-2a formats. See "
1890
"https://bugs.launchpad.net/bzr/+bug/375013 for details.")
1891
commit_builder_kls = vf_repository.VersionedFileCommitBuilder
1892
result = commit_builder_kls(self, parents, config,
1893
timestamp, timezone, committer, revprops, revision_id,
1895
self.start_write_group()
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)
1898
1227
def add_fallback_repository(self, repository):
1899
1228
"""Add a repository to use for looking up data not held locally.
1944
1272
delta, new_revision_id, parents, basis_inv=basis_inv,
1945
1273
propagate_caches=propagate_caches)
1947
def add_revision(self, revision_id, rev, inv=None):
1948
_mod_revision.check_not_reserved_id(revision_id)
1949
key = (revision_id,)
1950
# check inventory present
1951
if not self.inventories.get_parent_map([key]):
1953
raise errors.WeaveRevisionNotPresent(revision_id,
1956
# yes, this is not suitable for adding with ghosts.
1957
rev.inventory_sha1 = self.add_inventory(revision_id, inv,
1960
rev.inventory_sha1 = self.inventories.get_sha1s([key])[key]
1961
self._add_revision(rev)
1963
def _add_revision(self, rev):
1964
if self._real_repository is not None:
1965
return self._real_repository._add_revision(rev)
1966
text = self._serializer.write_revision_to_string(rev)
1967
key = (rev.revision_id,)
1968
parents = tuple((parent,) for parent in rev.parent_ids)
1969
self._write_group_tokens, missing_keys = self._get_sink().insert_stream(
1970
[('revisions', [FulltextContentFactory(key, parents, None, text)])],
1971
self._format, self._write_group_tokens)
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)
1973
1281
def get_inventory(self, revision_id):
1974
with self.lock_read():
1975
return list(self.iter_inventories([revision_id]))[0]
1977
def _iter_inventories_rpc(self, revision_ids, ordering):
1978
if ordering is None:
1979
ordering = 'unordered'
1980
path = self.controldir._path_for_remote_call(self._client)
1981
body = b"\n".join(revision_ids)
1982
response_tuple, response_handler = (
1983
self._call_with_body_bytes_expecting_body(
1984
b"VersionedFileRepository.get_inventories",
1985
(path, ordering.encode('ascii')), body))
1986
if response_tuple[0] != b"ok":
1987
raise errors.UnexpectedSmartServerResponse(response_tuple)
1988
deserializer = inventory_delta.InventoryDeltaDeserializer()
1989
byte_stream = response_handler.read_streamed_body()
1990
decoded = smart_repo._byte_stream_to_stream(byte_stream)
1992
# no results whatsoever
1994
src_format, stream = decoded
1995
if src_format.network_name() != self._format.network_name():
1996
raise AssertionError(
1997
"Mismatched RemoteRepository and stream src %r, %r" % (
1998
src_format.network_name(), self._format.network_name()))
1999
# ignore the src format, it's not really relevant
2000
prev_inv = Inventory(root_id=None,
2001
revision_id=_mod_revision.NULL_REVISION)
2002
# there should be just one substream, with inventory deltas
2003
substream_kind, substream = next(stream)
2004
if substream_kind != "inventory-deltas":
2005
raise AssertionError(
2006
"Unexpected stream %r received" % substream_kind)
2007
for record in substream:
2008
(parent_id, new_id, versioned_root, tree_references, invdelta) = (
2009
deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
2010
if parent_id != prev_inv.revision_id:
2011
raise AssertionError("invalid base %r != %r" % (parent_id,
2012
prev_inv.revision_id))
2013
inv = prev_inv.create_by_apply_delta(invdelta, new_id)
2014
yield inv, inv.revision_id
2017
def _iter_inventories_vfs(self, revision_ids, ordering=None):
2018
1282
self._ensure_real()
2019
return self._real_repository._iter_inventories(revision_ids, ordering)
1283
return self._real_repository.get_inventory(revision_id)
2021
1285
def iter_inventories(self, revision_ids, ordering=None):
2022
"""Get many inventories by revision_ids.
2024
This will buffer some or all of the texts used in constructing the
2025
inventories in memory, but will only parse a single inventory at a
2028
:param revision_ids: The expected revision ids of the inventories.
2029
:param ordering: optional ordering, e.g. 'topological'. If not
2030
specified, the order of revision_ids will be preserved (by
2031
buffering if necessary).
2032
:return: An iterator of inventories.
2034
if ((None in revision_ids)
2035
or (_mod_revision.NULL_REVISION in revision_ids)):
2036
raise ValueError('cannot get null revision inventory')
2037
for inv, revid in self._iter_inventories(revision_ids, ordering):
2039
raise errors.NoSuchRevision(self, revid)
2042
def _iter_inventories(self, revision_ids, ordering=None):
2043
if len(revision_ids) == 0:
2045
missing = set(revision_ids)
2046
if ordering is None:
2047
order_as_requested = True
2049
order = list(revision_ids)
2051
next_revid = order.pop()
2053
order_as_requested = False
2054
if ordering != 'unordered' and self._fallback_repositories:
2055
raise ValueError('unsupported ordering %r' % ordering)
2056
iter_inv_fns = [self._iter_inventories_rpc] + [
2057
fallback._iter_inventories for fallback in
2058
self._fallback_repositories]
2060
for iter_inv in iter_inv_fns:
2061
request = [revid for revid in revision_ids if revid in missing]
2062
for inv, revid in iter_inv(request, ordering):
2065
missing.remove(inv.revision_id)
2066
if ordering != 'unordered':
2070
if order_as_requested:
2071
# Yield as many results as we can while preserving order.
2072
while next_revid in invs:
2073
inv = invs.pop(next_revid)
2074
yield inv, inv.revision_id
2076
next_revid = order.pop()
2078
# We still want to fully consume the stream, just
2079
# in case it is not actually finished at this point
2082
except errors.UnknownSmartMethod:
2083
for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
2087
if order_as_requested:
2088
if next_revid is not None:
2089
yield None, next_revid
2092
yield invs.get(revid), revid
2095
yield None, missing.pop()
1287
return self._real_repository.iter_inventories(revision_ids, ordering)
2097
1290
def get_revision(self, revision_id):
2098
with self.lock_read():
2099
return self.get_revisions([revision_id])[0]
1292
return self._real_repository.get_revision(revision_id)
2101
1294
def get_transaction(self):
2102
1295
self._ensure_real()
2103
1296
return self._real_repository.get_transaction()
2105
def clone(self, a_controldir, revision_id=None):
2106
with self.lock_read():
2107
dest_repo = self._create_sprouting_repo(
2108
a_controldir, shared=self.is_shared())
2109
self.copy_content_into(dest_repo, revision_id)
1299
def clone(self, a_bzrdir, revision_id=None):
1301
return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
2112
1303
def make_working_trees(self):
2113
1304
"""See Repository.make_working_trees"""
2114
path = self.controldir._path_for_remote_call(self._client)
2116
response = self._call(b'Repository.make_working_trees', path)
2117
except errors.UnknownSmartMethod:
2119
return self._real_repository.make_working_trees()
2120
if response[0] not in (b'yes', b'no'):
2121
raise SmartProtocolError('unexpected response code %s' % (response,))
2122
return response[0] == b'yes'
1306
return self._real_repository.make_working_trees()
2124
1308
def refresh_data(self):
2125
"""Re-read any data needed to synchronise with disk.
1309
"""Re-read any data needed to to synchronise with disk.
2127
1311
This method is intended to be called after another repository instance
2128
1312
(such as one used by a smart server) has inserted data into the
2129
repository. On all repositories this will work outside of write groups.
2130
Some repository formats (pack and newer for breezy native formats)
2131
support refresh_data inside write groups. If called inside a write
2132
group on a repository that does not support refreshing in a write group
2133
IsInWriteGroupError will be raised.
1313
repository. It may not be called during a write group, but may be
1314
called at any other time.
1316
if self.is_in_write_group():
1317
raise errors.InternalBzrError(
1318
"May not refresh_data while in a write group.")
2135
1319
if self._real_repository is not None:
2136
1320
self._real_repository.refresh_data()
2137
# Refresh the parents cache for this object
2138
self._unstacked_provider.disable_cache()
2139
self._unstacked_provider.enable_cache()
2141
1322
def revision_ids_to_search_result(self, result_set):
2142
1323
"""Convert a set of revision ids to a graph SearchResult."""
2143
1324
result_parents = set()
2144
for parents in viewvalues(self.get_graph().get_parent_map(result_set)):
1325
for parents in self.get_graph().get_parent_map(
1326
result_set).itervalues():
2145
1327
result_parents.update(parents)
2146
1328
included_keys = result_set.intersection(result_parents)
2147
1329
start_keys = result_set.difference(included_keys)
2148
1330
exclude_keys = result_parents.difference(result_set)
2149
result = vf_search.SearchResult(start_keys, exclude_keys,
1331
result = graph.SearchResult(start_keys, exclude_keys,
2150
1332
len(result_set), result_set)
2153
def search_missing_revision_ids(self, other,
2154
find_ghosts=True, revision_ids=None, if_present_ids=None,
1336
def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2156
1337
"""Return the revision ids that other has that this does not.
2158
1339
These are returned in topological order.
2160
1341
revision_id: only return revision ids included by revision_id.
2162
with self.lock_read():
2163
inter_repo = _mod_repository.InterRepository.get(other, self)
2164
return inter_repo.search_missing_revision_ids(
2165
find_ghosts=find_ghosts, revision_ids=revision_ids,
2166
if_present_ids=if_present_ids, limit=limit)
1343
return repository.InterRepository.get(
1344
other, self).search_missing_revision_ids(revision_id, find_ghosts)
2168
def fetch(self, source, revision_id=None, find_ghosts=False,
1346
def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2169
1347
fetch_spec=None):
2170
1348
# No base implementation to use as RemoteRepository is not a subclass
2171
1349
# of Repository; so this is a copy of Repository.fetch().
2209
1388
return self._real_repository._get_versioned_file_checker(
2210
1389
revisions, revision_versions_cache)
2212
def _iter_files_bytes_rpc(self, desired_files, absent):
2213
path = self.controldir._path_for_remote_call(self._client)
2216
for (file_id, revid, identifier) in desired_files:
2217
lines.append(b''.join([
2218
osutils.safe_file_id(file_id),
2220
osutils.safe_revision_id(revid)]))
2221
identifiers.append(identifier)
2222
(response_tuple, response_handler) = (
2223
self._call_with_body_bytes_expecting_body(
2224
b"Repository.iter_files_bytes", (path, ), b"\n".join(lines)))
2225
if response_tuple != (b'ok', ):
2226
response_handler.cancel_read_body()
2227
raise errors.UnexpectedSmartServerResponse(response_tuple)
2228
byte_stream = response_handler.read_streamed_body()
2229
def decompress_stream(start, byte_stream, unused):
2230
decompressor = zlib.decompressobj()
2231
yield decompressor.decompress(start)
2232
while decompressor.unused_data == b"":
2234
data = next(byte_stream)
2235
except StopIteration:
2237
yield decompressor.decompress(data)
2238
yield decompressor.flush()
2239
unused.append(decompressor.unused_data)
2242
while not b"\n" in unused:
2243
unused += next(byte_stream)
2244
header, rest = unused.split(b"\n", 1)
2245
args = header.split(b"\0")
2246
if args[0] == b"absent":
2247
absent[identifiers[int(args[3])]] = (args[1], args[2])
2250
elif args[0] == b"ok":
2253
raise errors.UnexpectedSmartServerResponse(args)
2255
yield (identifiers[idx],
2256
decompress_stream(rest, byte_stream, unused_chunks))
2257
unused = b"".join(unused_chunks)
2259
1391
def iter_files_bytes(self, desired_files):
2260
1392
"""See Repository.iter_file_bytes.
2264
for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2265
desired_files, absent):
2266
yield identifier, bytes_iterator
2267
for fallback in self._fallback_repositories:
2270
desired_files = [(key[0], key[1], identifier)
2271
for identifier, key in viewitems(absent)]
2272
for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2273
del absent[identifier]
2274
yield identifier, bytes_iterator
2276
# There may be more missing items, but raise an exception
2278
missing_identifier = next(iter(absent))
2279
missing_key = absent[missing_identifier]
2280
raise errors.RevisionNotPresent(revision_id=missing_key[1],
2281
file_id=missing_key[0])
2282
except errors.UnknownSmartMethod:
2284
for (identifier, bytes_iterator) in (
2285
self._real_repository.iter_files_bytes(desired_files)):
2286
yield identifier, bytes_iterator
2288
def get_cached_parent_map(self, revision_ids):
2289
"""See breezy.CachingParentsProvider.get_cached_parent_map"""
2290
return self._unstacked_provider.get_cached_parent_map(revision_ids)
1395
return self._real_repository.iter_files_bytes(desired_files)
2292
1397
def get_parent_map(self, revision_ids):
2293
"""See breezy.Graph.get_parent_map()."""
1398
"""See bzrlib.Graph.get_parent_map()."""
2294
1399
return self._make_parents_provider().get_parent_map(revision_ids)
2296
1401
def _get_parent_map_rpc(self, keys):
2413
1529
revision_graph[d[0]] = (NULL_REVISION,)
2414
1530
return revision_graph
2416
1533
def get_signature_text(self, revision_id):
2417
with self.lock_read():
2418
path = self.controldir._path_for_remote_call(self._client)
2420
response_tuple, response_handler = self._call_expecting_body(
2421
b'Repository.get_revision_signature_text', path, revision_id)
2422
except errors.UnknownSmartMethod:
2424
return self._real_repository.get_signature_text(revision_id)
2425
except errors.NoSuchRevision as err:
2426
for fallback in self._fallback_repositories:
2428
return fallback.get_signature_text(revision_id)
2429
except errors.NoSuchRevision:
2433
if response_tuple[0] != b'ok':
2434
raise errors.UnexpectedSmartServerResponse(response_tuple)
2435
return response_handler.read_body_bytes()
1535
return self._real_repository.get_signature_text(revision_id)
2437
1538
def _get_inventory_xml(self, revision_id):
2438
with self.lock_read():
2439
# This call is used by older working tree formats,
2440
# which stored a serialized basis inventory.
2442
return self._real_repository._get_inventory_xml(revision_id)
1540
return self._real_repository._get_inventory_xml(revision_id)
2444
1542
def reconcile(self, other=None, thorough=False):
2445
from ..reconcile import RepoReconciler
2446
with self.lock_write():
2447
path = self.controldir._path_for_remote_call(self._client)
2449
response, handler = self._call_expecting_body(
2450
b'Repository.reconcile', path, self._lock_token)
2451
except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2453
return self._real_repository.reconcile(other=other, thorough=thorough)
2454
if response != (b'ok', ):
2455
raise errors.UnexpectedSmartServerResponse(response)
2456
body = handler.read_body_bytes()
2457
result = RepoReconciler(self)
2458
for line in body.split(b'\n'):
2461
key, val_text = line.split(b':')
2462
if key == b"garbage_inventories":
2463
result.garbage_inventories = int(val_text)
2464
elif key == b"inconsistent_parents":
2465
result.inconsistent_parents = int(val_text)
2467
mutter("unknown reconcile key %r" % key)
1544
return self._real_repository.reconcile(other=other, thorough=thorough)
2470
1546
def all_revision_ids(self):
2471
path = self.controldir._path_for_remote_call(self._client)
2473
response_tuple, response_handler = self._call_expecting_body(
2474
b"Repository.all_revision_ids", path)
2475
except errors.UnknownSmartMethod:
2477
return self._real_repository.all_revision_ids()
2478
if response_tuple != (b"ok", ):
2479
raise errors.UnexpectedSmartServerResponse(response_tuple)
2480
revids = set(response_handler.read_body_bytes().splitlines())
2481
for fallback in self._fallback_repositories:
2482
revids.update(set(fallback.all_revision_ids()))
2485
def _filtered_revision_trees(self, revision_ids, file_ids):
2486
"""Return Tree for a revision on this branch with only some files.
2488
:param revision_ids: a sequence of revision-ids;
2489
a revision-id may not be None or b'null:'
2490
:param file_ids: if not None, the result is filtered
2491
so that only those file-ids, their parents and their
2492
children are included.
2494
inventories = self.iter_inventories(revision_ids)
2495
for inv in inventories:
2496
# Should we introduce a FilteredRevisionTree class rather
2497
# than pre-filter the inventory here?
2498
filtered_inv = inv.filter(file_ids)
2499
yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
1548
return self._real_repository.all_revision_ids()
2501
1551
def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2502
with self.lock_read():
2503
medium = self._client._medium
2504
if medium._is_remote_before((1, 2)):
2506
for delta in self._real_repository.get_deltas_for_revisions(
2507
revisions, specific_fileids):
2510
# Get the revision-ids of interest
2511
required_trees = set()
2512
for revision in revisions:
2513
required_trees.add(revision.revision_id)
2514
required_trees.update(revision.parent_ids[:1])
2516
# Get the matching filtered trees. Note that it's more
2517
# efficient to pass filtered trees to changes_from() rather
2518
# than doing the filtering afterwards. changes_from() could
2519
# arguably do the filtering itself but it's path-based, not
2520
# file-id based, so filtering before or afterwards is
2522
if specific_fileids is None:
2523
trees = dict((t.get_revision_id(), t) for
2524
t in self.revision_trees(required_trees))
2526
trees = dict((t.get_revision_id(), t) for
2527
t in self._filtered_revision_trees(required_trees,
2530
# Calculate the deltas
2531
for revision in revisions:
2532
if not revision.parent_ids:
2533
old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2535
old_tree = trees[revision.parent_ids[0]]
2536
yield trees[revision.revision_id].changes_from(old_tree)
1553
return self._real_repository.get_deltas_for_revisions(revisions,
1554
specific_fileids=specific_fileids)
2538
1557
def get_revision_delta(self, revision_id, specific_fileids=None):
2539
with self.lock_read():
2540
r = self.get_revision(revision_id)
2541
return list(self.get_deltas_for_revisions([r],
2542
specific_fileids=specific_fileids))[0]
1559
return self._real_repository.get_revision_delta(revision_id,
1560
specific_fileids=specific_fileids)
2544
1563
def revision_trees(self, revision_ids):
2545
with self.lock_read():
2546
inventories = self.iter_inventories(revision_ids)
2547
for inv in inventories:
2548
yield RemoteInventoryTree(self, inv, inv.revision_id)
1565
return self._real_repository.revision_trees(revision_ids)
2550
1568
def get_revision_reconcile(self, revision_id):
2551
with self.lock_read():
2553
return self._real_repository.get_revision_reconcile(revision_id)
1570
return self._real_repository.get_revision_reconcile(revision_id)
2555
1573
def check(self, revision_ids=None, callback_refs=None, check_repo=True):
2556
with self.lock_read():
2558
return self._real_repository.check(revision_ids=revision_ids,
2559
callback_refs=callback_refs, check_repo=check_repo)
1575
return self._real_repository.check(revision_ids=revision_ids,
1576
callback_refs=callback_refs, check_repo=check_repo)
2561
1578
def copy_content_into(self, destination, revision_id=None):
2562
"""Make a complete copy of the content in self into destination.
2564
This is a destructive operation! Do not use it on existing
2567
interrepo = _mod_repository.InterRepository.get(self, destination)
2568
return interrepo.copy_content(revision_id)
1580
return self._real_repository.copy_content_into(
1581
destination, revision_id=revision_id)
2570
1583
def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2571
1584
# get a tarball of the remote repository, and copy from that into the
1586
from bzrlib import osutils
2574
1588
# TODO: Maybe a progress bar while streaming the tarball?
2575
note(gettext("Copying repository content as tarball..."))
1589
note("Copying repository content as tarball...")
2576
1590
tar_file = self._get_tarball('bz2')
2577
1591
if tar_file is None:
2676
1680
self._ensure_real()
2677
1681
return self._real_repository.texts
2679
def _iter_revisions_rpc(self, revision_ids):
2680
body = b"\n".join(revision_ids)
2681
path = self.controldir._path_for_remote_call(self._client)
2682
response_tuple, response_handler = (
2683
self._call_with_body_bytes_expecting_body(
2684
b"Repository.iter_revisions", (path, ), body))
2685
if response_tuple[0] != b"ok":
2686
raise errors.UnexpectedSmartServerResponse(response_tuple)
2687
serializer_format = response_tuple[1].decode('ascii')
2688
serializer = serializer_format_registry.get(serializer_format)
2689
byte_stream = response_handler.read_streamed_body()
2690
decompressor = zlib.decompressobj()
2692
for bytes in byte_stream:
2693
chunks.append(decompressor.decompress(bytes))
2694
if decompressor.unused_data != b"":
2695
chunks.append(decompressor.flush())
2696
yield serializer.read_revision_from_string(b"".join(chunks))
2697
unused = decompressor.unused_data
2698
decompressor = zlib.decompressobj()
2699
chunks = [decompressor.decompress(unused)]
2700
chunks.append(decompressor.flush())
2701
text = b"".join(chunks)
2703
yield serializer.read_revision_from_string(b"".join(chunks))
2705
def iter_revisions(self, revision_ids):
2706
for rev_id in revision_ids:
2707
if not rev_id or not isinstance(rev_id, bytes):
2708
raise errors.InvalidRevisionId(
2709
revision_id=rev_id, branch=self)
2710
with self.lock_read():
2712
missing = set(revision_ids)
2713
for rev in self._iter_revisions_rpc(revision_ids):
2714
missing.remove(rev.revision_id)
2715
yield (rev.revision_id, rev)
2716
for fallback in self._fallback_repositories:
2719
for (revid, rev) in fallback.iter_revisions(missing):
2722
missing.remove(revid)
2723
for revid in missing:
2725
except errors.UnknownSmartMethod:
2727
for entry in self._real_repository.iter_revisions(revision_ids):
1684
def get_revisions(self, revision_ids):
1686
return self._real_repository.get_revisions(revision_ids)
2730
1688
def supports_rich_root(self):
2731
1689
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)
2734
1696
def _serializer(self):
2735
1697
return self._format._serializer
2737
1699
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2738
with self.lock_write():
2739
signature = gpg_strategy.sign(plaintext, gpg.MODE_CLEAR)
2740
self.add_signature_text(revision_id, signature)
1701
return self._real_repository.store_revision_signature(
1702
gpg_strategy, plaintext, revision_id)
2742
1704
def add_signature_text(self, revision_id, signature):
2743
if self._real_repository:
2744
# If there is a real repository the write group will
2745
# be in the real repository as well, so use that:
2747
return self._real_repository.add_signature_text(
2748
revision_id, signature)
2749
path = self.controldir._path_for_remote_call(self._client)
2750
response, handler = self._call_with_body_bytes_expecting_body(
2751
b'Repository.add_signature_text', (path, self._lock_token,
2752
revision_id) + tuple(self._write_group_tokens), signature)
2753
handler.cancel_read_body()
2755
if response[0] != b'ok':
2756
raise errors.UnexpectedSmartServerResponse(response)
2757
self._write_group_tokens = response[1:]
1706
return self._real_repository.add_signature_text(revision_id, signature)
2759
1708
def has_signature_for_revision_id(self, revision_id):
2760
path = self.controldir._path_for_remote_call(self._client)
2762
response = self._call(b'Repository.has_signature_for_revision_id',
2764
except errors.UnknownSmartMethod:
2766
return self._real_repository.has_signature_for_revision_id(
2768
if response[0] not in (b'yes', b'no'):
2769
raise SmartProtocolError('unexpected response code %s' % (response,))
2770
if response[0] == b'yes':
2772
for fallback in self._fallback_repositories:
2773
if fallback.has_signature_for_revision_id(revision_id):
2777
def verify_revision_signature(self, revision_id, gpg_strategy):
2778
with self.lock_read():
2779
if not self.has_signature_for_revision_id(revision_id):
2780
return gpg.SIGNATURE_NOT_SIGNED, None
2781
signature = self.get_signature_text(revision_id)
2783
testament = _mod_testament.Testament.from_revision(self, revision_id)
2785
(status, key, signed_plaintext) = gpg_strategy.verify(signature)
2786
if testament.as_short_text() != signed_plaintext:
2787
return gpg.SIGNATURE_NOT_VALID, None
2788
return (status, key)
1710
return self._real_repository.has_signature_for_revision_id(revision_id)
2790
1712
def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2791
1713
self._ensure_real()
2792
1714
return self._real_repository.item_keys_introduced_by(revision_ids,
2793
1715
_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()
2795
1722
def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2796
1723
self._ensure_real()
2797
1724
return self._real_repository._find_inconsistent_revision_parents(
2814
1742
:param recipe: A search recipe (start, stop, count).
2815
1743
:return: Serialised bytes.
2817
start_keys = b' '.join(recipe[1])
2818
stop_keys = b' '.join(recipe[2])
2819
count = str(recipe[3]).encode('ascii')
2820
return b'\n'.join((start_keys, stop_keys, count))
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))
2822
1750
def _serialise_search_result(self, search_result):
2823
parts = search_result.get_network_struct()
2824
return b'\n'.join(parts)
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)
2826
1759
def autopack(self):
2827
path = self.controldir._path_for_remote_call(self._client)
1760
path = self.bzrdir._path_for_remote_call(self._client)
2829
response = self._call(b'PackRepository.autopack', path)
1762
response = self._call('PackRepository.autopack', path)
2830
1763
except errors.UnknownSmartMethod:
2831
1764
self._ensure_real()
2832
1765
self._real_repository._pack_collection.autopack()
2834
1767
self.refresh_data()
2835
if response[0] != b'ok':
2836
raise errors.UnexpectedSmartServerResponse(response)
2838
def _revision_archive(self, revision_id, format, name, root, subdir,
2840
path = self.controldir._path_for_remote_call(self._client)
2841
format = format or ''
2843
subdir = subdir or ''
2844
force_mtime = int(force_mtime) if force_mtime is not None else None
2846
response, protocol = self._call_expecting_body(
2847
b'Repository.revision_archive', path,
2849
format.encode('ascii'),
2850
os.path.basename(name).encode('utf-8'),
2851
root.encode('utf-8'),
2852
subdir.encode('utf-8'),
2854
except errors.UnknownSmartMethod:
2856
if response[0] == b'ok':
2857
return iter([protocol.read_body_bytes()])
2858
raise errors.UnexpectedSmartServerResponse(response)
2860
def _annotate_file_revision(self, revid, tree_path, file_id, default_revision):
2861
path = self.controldir._path_for_remote_call(self._client)
2862
tree_path = tree_path.encode('utf-8')
2863
file_id = file_id or b''
2864
default_revision = default_revision or b''
2866
response, handler = self._call_expecting_body(
2867
b'Repository.annotate_file_revision', path,
2868
revid, tree_path, file_id, default_revision)
2869
except errors.UnknownSmartMethod:
2871
if response[0] != b'ok':
2872
raise errors.UnexpectedSmartServerResponse(response)
2873
return map(tuple, bencode.bdecode(handler.read_body_bytes()))
2876
class RemoteStreamSink(vf_repository.StreamSink):
1768
if response[0] != 'ok':
1769
raise errors.UnexpectedSmartServerResponse(response)
1772
class RemoteStreamSink(repository.StreamSink):
2878
1774
def _insert_real(self, stream, src_format, resume_tokens):
2879
1775
self.target_repo._ensure_real()
3021
1906
sources.append(repo)
3022
1907
return self.missing_parents_chain(search, sources)
3024
def _get_real_stream_for_missing_keys(self, missing_keys):
1909
def get_stream_for_missing_keys(self, missing_keys):
3025
1910
self.from_repository._ensure_real()
3026
1911
real_repo = self.from_repository._real_repository
3027
1912
real_source = real_repo._get_source(self.to_format)
3028
1913
return real_source.get_stream_for_missing_keys(missing_keys)
3030
def get_stream_for_missing_keys(self, missing_keys):
3031
if not isinstance(self.from_repository, RemoteRepository):
3032
return self._get_real_stream_for_missing_keys(missing_keys)
3033
client = self.from_repository._client
3034
medium = client._medium
3035
if medium._is_remote_before((3, 0)):
3036
return self._get_real_stream_for_missing_keys(missing_keys)
3037
path = self.from_repository.controldir._path_for_remote_call(client)
3038
args = (path, self.to_format.network_name())
3039
search_bytes = b'\n'.join([b'\t'.join(key) for key in missing_keys])
3041
response, handler = self.from_repository._call_with_body_bytes_expecting_body(
3042
b'Repository.get_stream_for_missing_keys', args, search_bytes)
3043
except (errors.UnknownSmartMethod, errors.UnknownFormatError):
3044
return self._get_real_stream_for_missing_keys(missing_keys)
3045
if response[0] != b'ok':
3046
raise errors.UnexpectedSmartServerResponse(response)
3047
byte_stream = handler.read_streamed_body()
3048
src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
3049
self._record_counter)
3050
if src_format.network_name() != self.from_repository._format.network_name():
3051
raise AssertionError(
3052
"Mismatched RemoteRepository and stream src %r, %r" % (
3053
src_format.network_name(), repo._format.network_name()))
3056
1915
def _real_stream(self, repo, search):
3057
1916
"""Get a stream for search from repo.
3059
This never called RemoteStreamSource.get_stream, and is a helper
3060
for RemoteStreamSource._get_stream to allow getting a stream
1918
This never called RemoteStreamSource.get_stream, and is a heler
1919
for RemoteStreamSource._get_stream to allow getting a stream
3061
1920
reliably whether fallback back because of old servers or trying
3062
1921
to stream from a non-RemoteRepository (which the stacked support
3217
2059
def network_name(self):
3218
2060
return self._network_name
3220
def open(self, a_controldir, name=None, ignore_fallbacks=False):
3221
return a_controldir.open_branch(name=name,
2062
def open(self, a_bzrdir, name=None, ignore_fallbacks=False):
2063
return a_bzrdir.open_branch(name=name,
3222
2064
ignore_fallbacks=ignore_fallbacks)
3224
def _vfs_initialize(self, a_controldir, name, append_revisions_only,
2066
def _vfs_initialize(self, a_bzrdir, name):
3226
2067
# Initialisation when using a local bzrdir object, or a non-vfs init
3227
2068
# method is not available on the server.
3228
2069
# self._custom_format is always set - the start of initialize ensures
3230
if isinstance(a_controldir, RemoteBzrDir):
3231
a_controldir._ensure_real()
3232
result = self._custom_format.initialize(a_controldir._real_bzrdir,
3233
name=name, append_revisions_only=append_revisions_only,
3234
repository=repository)
2071
if isinstance(a_bzrdir, RemoteBzrDir):
2072
a_bzrdir._ensure_real()
2073
result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3236
2076
# We assume the bzrdir is parameterised; it may not be.
3237
result = self._custom_format.initialize(a_controldir, name=name,
3238
append_revisions_only=append_revisions_only,
3239
repository=repository)
3240
if (isinstance(a_controldir, RemoteBzrDir) and
2077
result = self._custom_format.initialize(a_bzrdir, name)
2078
if (isinstance(a_bzrdir, RemoteBzrDir) and
3241
2079
not isinstance(result, RemoteBranch)):
3242
result = RemoteBranch(a_controldir, a_controldir.find_repository(), result,
2080
result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3246
def initialize(self, a_controldir, name=None, repository=None,
3247
append_revisions_only=None):
3249
name = a_controldir._get_selected_branch()
2084
def initialize(self, a_bzrdir, name=None):
3250
2085
# 1) get the network name to use.
3251
2086
if self._custom_format:
3252
2087
network_name = self._custom_format.network_name()
3254
# Select the current breezy default and ask for that.
3255
reference_bzrdir_format = controldir.format_registry.get('default')()
2089
# Select the current bzrlib default and ask for that.
2090
reference_bzrdir_format = bzrdir.format_registry.get('default')()
3256
2091
reference_format = reference_bzrdir_format.get_branch_format()
3257
2092
self._custom_format = reference_format
3258
2093
network_name = reference_format.network_name()
3259
2094
# Being asked to create on a non RemoteBzrDir:
3260
if not isinstance(a_controldir, RemoteBzrDir):
3261
return self._vfs_initialize(a_controldir, name=name,
3262
append_revisions_only=append_revisions_only,
3263
repository=repository)
3264
medium = a_controldir._client._medium
2095
if not isinstance(a_bzrdir, RemoteBzrDir):
2096
return self._vfs_initialize(a_bzrdir, name=name)
2097
medium = a_bzrdir._client._medium
3265
2098
if medium._is_remote_before((1, 13)):
3266
return self._vfs_initialize(a_controldir, name=name,
3267
append_revisions_only=append_revisions_only,
3268
repository=repository)
2099
return self._vfs_initialize(a_bzrdir, name=name)
3269
2100
# Creating on a remote bzr dir.
3270
2101
# 2) try direct creation via RPC
3271
path = a_controldir._path_for_remote_call(a_controldir._client)
2102
path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
2103
if name is not None:
3273
2104
# XXX JRV20100304: Support creating colocated branches
3274
2105
raise errors.NoColocatedBranchSupport(self)
3275
verb = b'BzrDir.create_branch'
2106
verb = 'BzrDir.create_branch'
3277
response = a_controldir._call(verb, path, network_name)
2108
response = a_bzrdir._call(verb, path, network_name)
3278
2109
except errors.UnknownSmartMethod:
3279
2110
# Fallback - use vfs methods
3280
2111
medium._remember_remote_is_before((1, 13))
3281
return self._vfs_initialize(a_controldir, name=name,
3282
append_revisions_only=append_revisions_only,
3283
repository=repository)
3284
if response[0] != b'ok':
2112
return self._vfs_initialize(a_bzrdir, name=name)
2113
if response[0] != 'ok':
3285
2114
raise errors.UnexpectedSmartServerResponse(response)
3286
2115
# Turn the response into a RemoteRepository object.
3287
2116
format = RemoteBranchFormat(network_name=response[1])
3288
2117
repo_format = response_tuple_to_repo_format(response[3:])
3289
repo_path = response[2].decode('utf-8')
3290
if repository is not None:
3291
remote_repo_url = urlutils.join(a_controldir.user_url, repo_path)
3292
url_diff = urlutils.relative_url(repository.user_url,
3295
raise AssertionError(
3296
'repository.user_url %r does not match URL from server '
3297
'response (%r + %r)'
3298
% (repository.user_url, a_controldir.user_url, repo_path))
3299
remote_repo = repository
2118
if response[2] == '':
2119
repo_bzrdir = a_bzrdir
3302
repo_bzrdir = a_controldir
3304
repo_bzrdir = RemoteBzrDir(
3305
a_controldir.root_transport.clone(repo_path), a_controldir._format,
3306
a_controldir._client)
3307
remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3308
remote_branch = RemoteBranch(a_controldir, remote_repo,
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,
3309
2126
format=format, setup_stacking=False, name=name)
3310
if append_revisions_only:
3311
remote_branch.set_append_revisions_only(append_revisions_only)
3312
2127
# XXX: We know this is a new branch, so it must have revno 0, revid
3313
2128
# NULL_REVISION. Creating the branch locked would make this be unable
3314
2129
# to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3333
2148
self._ensure_real()
3334
2149
return self._custom_format.supports_set_append_revisions_only()
3336
def _use_default_local_heads_to_fetch(self):
3337
# If the branch format is a metadir format *and* its heads_to_fetch
3338
# implementation is not overridden vs the base class, we can use the
3339
# base class logic rather than use the heads_to_fetch RPC. This is
3340
# usually cheaper in terms of net round trips, as the last-revision and
3341
# tags info fetched is cached and would be fetched anyway.
3343
if isinstance(self._custom_format, bzrbranch.BranchFormatMetadir):
3344
branch_class = self._custom_format._branch_class()
3345
heads_to_fetch_impl = get_unbound_function(branch_class.heads_to_fetch)
3346
if heads_to_fetch_impl is get_unbound_function(branch.Branch.heads_to_fetch):
3351
class RemoteBranchStore(_mod_config.IniFileStore):
3352
"""Branch store which attempts to use HPSS calls to retrieve branch store.
3354
Note that this is specific to bzr-based formats.
3357
def __init__(self, branch):
3358
super(RemoteBranchStore, self).__init__()
3359
self.branch = branch
3361
self._real_store = None
3363
def external_url(self):
3364
return urlutils.join(self.branch.user_url, 'branch.conf')
3366
def _load_content(self):
3367
path = self.branch._remote_path()
3369
response, handler = self.branch._call_expecting_body(
3370
b'Branch.get_config_file', path)
3371
except errors.UnknownSmartMethod:
3373
return self._real_store._load_content()
3374
if len(response) and response[0] != b'ok':
3375
raise errors.UnexpectedSmartServerResponse(response)
3376
return handler.read_body_bytes()
3378
def _save_content(self, content):
3379
path = self.branch._remote_path()
3381
response, handler = self.branch._call_with_body_bytes_expecting_body(
3382
b'Branch.put_config_file', (path,
3383
self.branch._lock_token, self.branch._repo_lock_token),
3385
except errors.UnknownSmartMethod:
3387
return self._real_store._save_content(content)
3388
handler.cancel_read_body()
3389
if response != (b'ok', ):
3390
raise errors.UnexpectedSmartServerResponse(response)
3392
def _ensure_real(self):
3393
self.branch._ensure_real()
3394
if self._real_store is None:
3395
self._real_store = _mod_config.BranchStore(self.branch)
3398
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3399
2153
"""Branch stored on a server accessed by HPSS RPC.
3940
2644
self._ensure_real()
3941
2645
return self._real_branch._set_parent_location(url)
3943
2648
def pull(self, source, overwrite=False, stop_revision=None,
3945
with self.lock_write():
3946
self._clear_cached_state_of_remote_branch_only()
3948
return self._real_branch.pull(
3949
source, overwrite=overwrite, stop_revision=stop_revision,
3950
_override_hook_target=self, **kwargs)
3952
def push(self, target, overwrite=False, stop_revision=None, lossy=False):
3953
with self.lock_read():
3955
return self._real_branch.push(
3956
target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
3957
_override_hook_source_branch=self)
3959
def peek_lock_mode(self):
3960
return self._lock_mode
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)
3962
2663
def is_locked(self):
3963
2664
return self._lock_count >= 1
3965
def revision_id_to_dotted_revno(self, revision_id):
3966
"""Given a revision id, return its dotted revno.
3968
:return: a tuple like (1,) or (400,1,3).
3970
with self.lock_read():
3972
response = self._call(b'Branch.revision_id_to_revno',
3973
self._remote_path(), revision_id)
3974
except errors.UnknownSmartMethod:
3976
return self._real_branch.revision_id_to_dotted_revno(revision_id)
3977
if response[0] == b'ok':
3978
return tuple([int(x) for x in response[1:]])
3980
raise errors.UnexpectedSmartServerResponse(response)
3982
2667
def revision_id_to_revno(self, revision_id):
3983
"""Given a revision id on the branch mainline, return its revno.
3987
with self.lock_read():
3989
response = self._call(b'Branch.revision_id_to_revno',
3990
self._remote_path(), revision_id)
3991
except errors.UnknownSmartMethod:
3993
return self._real_branch.revision_id_to_revno(revision_id)
3994
if response[0] == b'ok':
3995
if len(response) == 2:
3996
return int(response[1])
3997
raise NoSuchRevision(self, revision_id)
3999
raise errors.UnexpectedSmartServerResponse(response)
2669
return self._real_branch.revision_id_to_revno(revision_id)
4001
2672
def set_last_revision_info(self, revno, revision_id):
4002
with self.lock_write():
4003
# XXX: These should be returned by the set_last_revision_info verb
4004
old_revno, old_revid = self.last_revision_info()
4005
self._run_pre_change_branch_tip_hooks(revno, revision_id)
4006
if not revision_id or not isinstance(revision_id, bytes):
4007
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
4009
response = self._call(b'Branch.set_last_revision_info',
4010
self._remote_path(), self._lock_token, self._repo_lock_token,
4011
str(revno).encode('ascii'), revision_id)
4012
except errors.UnknownSmartMethod:
4014
self._clear_cached_state_of_remote_branch_only()
4015
self._real_branch.set_last_revision_info(revno, revision_id)
4016
self._last_revision_info_cache = revno, revision_id
4018
if response == (b'ok',):
4019
self._clear_cached_state()
4020
self._last_revision_info_cache = revno, revision_id
4021
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
4022
# Update the _real_branch's cache too.
4023
if self._real_branch is not None:
4024
cache = self._last_revision_info_cache
4025
self._real_branch._last_revision_info_cache = cache
4027
raise errors.UnexpectedSmartServerResponse(response)
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)
4029
2699
def generate_revision_history(self, revision_id, last_rev=None,
4030
2700
other_branch=None):
4031
with self.lock_write():
4032
medium = self._client._medium
4033
if not medium._is_remote_before((1, 6)):
4034
# Use a smart method for 1.6 and above servers
4036
self._set_last_revision_descendant(revision_id, other_branch,
4037
allow_diverged=True, allow_overwrite_descendant=True)
4039
except errors.UnknownSmartMethod:
4040
medium._remember_remote_is_before((1, 6))
4041
self._clear_cached_state_of_remote_branch_only()
4042
graph = self.repository.get_graph()
4043
(last_revno, last_revid) = self.last_revision_info()
4044
known_revision_ids = [
4045
(last_revid, last_revno),
4046
(_mod_revision.NULL_REVISION, 0),
4048
if last_rev is not None:
4049
if not graph.is_ancestor(last_rev, revision_id):
4050
# our previous tip is not merged into stop_revision
4051
raise errors.DivergedBranches(self, other_branch)
4052
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
4053
self.set_last_revision_info(revno, revision_id)
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
2714
def set_push_location(self, location):
4056
self._set_config_location('push_location', location)
4058
def heads_to_fetch(self):
4059
if self._format._use_default_local_heads_to_fetch():
4060
# We recognise this format, and its heads-to-fetch implementation
4061
# is the default one (tip + tags). In this case it's cheaper to
4062
# just use the default implementation rather than a special RPC as
4063
# the tip and tags data is cached.
4064
return branch.Branch.heads_to_fetch(self)
4065
medium = self._client._medium
4066
if medium._is_remote_before((2, 4)):
4067
return self._vfs_heads_to_fetch()
4069
return self._rpc_heads_to_fetch()
4070
except errors.UnknownSmartMethod:
4071
medium._remember_remote_is_before((2, 4))
4072
return self._vfs_heads_to_fetch()
4074
def _rpc_heads_to_fetch(self):
4075
response = self._call(b'Branch.heads_to_fetch', self._remote_path())
4076
if len(response) != 2:
4077
raise errors.UnexpectedSmartServerResponse(response)
4078
must_fetch, if_present_fetch = response
4079
return set(must_fetch), set(if_present_fetch)
4081
def _vfs_heads_to_fetch(self):
4082
2715
self._ensure_real()
4083
return self._real_branch.heads_to_fetch()
2716
return self._real_branch.set_push_location(location)
4086
2719
class RemoteConfig(object):
4151
2774
medium = self._branch._client._medium
4152
2775
if medium._is_remote_before((1, 14)):
4153
2776
return self._vfs_set_option(value, name, section)
4154
if isinstance(value, dict):
4155
if medium._is_remote_before((2, 2)):
4156
return self._vfs_set_option(value, name, section)
4157
return self._set_config_option_dict(value, name, section)
4159
return self._set_config_option(value, name, section)
4161
def _set_config_option(self, value, name, section):
4163
2778
path = self._branch._remote_path()
4164
response = self._branch._client.call(b'Branch.set_config_option',
2779
response = self._branch._client.call('Branch.set_config_option',
4165
2780
path, self._branch._lock_token, self._branch._repo_lock_token,
4166
value.encode('utf8'), name.encode('utf-8'),
4167
(section or '').encode('utf-8'))
2781
value.encode('utf8'), name, section or '')
4168
2782
except errors.UnknownSmartMethod:
4169
medium = self._branch._client._medium
4170
2783
medium._remember_remote_is_before((1, 14))
4171
2784
return self._vfs_set_option(value, name, section)
4172
2785
if response != ():
4173
2786
raise errors.UnexpectedSmartServerResponse(response)
4175
def _serialize_option_dict(self, option_dict):
4177
for key, value in option_dict.items():
4178
if isinstance(key, text_type):
4179
key = key.encode('utf8')
4180
if isinstance(value, text_type):
4181
value = value.encode('utf8')
4182
utf8_dict[key] = value
4183
return bencode.bencode(utf8_dict)
4185
def _set_config_option_dict(self, value, name, section):
4187
path = self._branch._remote_path()
4188
serialised_dict = self._serialize_option_dict(value)
4189
response = self._branch._client.call(
4190
b'Branch.set_config_option_dict',
4191
path, self._branch._lock_token, self._branch._repo_lock_token,
4192
serialised_dict, name.encode('utf-8'), (section or '').encode('utf-8'))
4193
except errors.UnknownSmartMethod:
4194
medium = self._branch._client._medium
4195
medium._remember_remote_is_before((2, 2))
4196
return self._vfs_set_option(value, name, section)
4198
raise errors.UnexpectedSmartServerResponse(response)
4200
2788
def _real_object(self):
4201
2789
self._branch._ensure_real()
4202
2790
return self._branch._real_branch
4273
2867
return context['path']
2868
except KeyError, key_err:
4276
return err.error_args[0].decode('utf-8')
4278
mutter('Missing key \'path\' in context %r', context)
2870
return err.error_args[0]
2871
except IndexError, idx_err:
2873
'Missing key %r in context %r', key_err.args[0], context)
4280
if not isinstance(err.error_verb, bytes):
4281
raise TypeError(err.error_verb)
4283
translator = error_translators.get(err.error_verb)
4287
raise translator(err, find, get_path)
4289
translator = no_context_error_translators.get(err.error_verb)
4291
raise errors.UnknownErrorFromSmartServer(err)
4293
raise translator(err)
4296
error_translators.register(b'NoSuchRevision',
4297
lambda err, find, get_path: NoSuchRevision(
4298
find('branch'), err.error_args[0]))
4299
error_translators.register(b'nosuchrevision',
4300
lambda err, find, get_path: NoSuchRevision(
4301
find('repository'), err.error_args[0]))
4303
def _translate_nobranch_error(err, find, get_path):
4304
if len(err.error_args) >= 1:
4305
extra = err.error_args[0].decode('utf-8')
4308
return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4311
error_translators.register(b'nobranch', _translate_nobranch_error)
4312
error_translators.register(b'norepository',
4313
lambda err, find, get_path: errors.NoRepositoryPresent(
4315
error_translators.register(b'UnlockableTransport',
4316
lambda err, find, get_path: errors.UnlockableTransport(
4317
find('bzrdir').root_transport))
4318
error_translators.register(b'TokenMismatch',
4319
lambda err, find, get_path: errors.TokenMismatch(
4320
find('token'), '(remote token)'))
4321
error_translators.register(b'Diverged',
4322
lambda err, find, get_path: errors.DivergedBranches(
4323
find('branch'), find('other_branch')))
4324
error_translators.register(b'NotStacked',
4325
lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4327
def _translate_PermissionDenied(err, find, get_path):
4329
if len(err.error_args) >= 2:
4330
extra = err.error_args[1].decode('utf-8')
4333
return errors.PermissionDenied(path, extra=extra)
4335
error_translators.register(b'PermissionDenied', _translate_PermissionDenied)
4336
error_translators.register(b'ReadError',
4337
lambda err, find, get_path: errors.ReadError(get_path()))
4338
error_translators.register(b'NoSuchFile',
4339
lambda err, find, get_path: errors.NoSuchFile(get_path()))
4340
error_translators.register(b'TokenLockingNotSupported',
4341
lambda err, find, get_path: errors.TokenLockingNotSupported(
4342
find('repository')))
4343
error_translators.register(b'UnsuspendableWriteGroup',
4344
lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4345
repository=find('repository')))
4346
error_translators.register(b'UnresumableWriteGroup',
4347
lambda err, find, get_path: errors.UnresumableWriteGroup(
4348
repository=find('repository'), write_groups=err.error_args[0],
4349
reason=err.error_args[1]))
4350
no_context_error_translators.register(b'GhostRevisionsHaveNoRevno',
4351
lambda err: errors.GhostRevisionsHaveNoRevno(*err.error_args))
4352
no_context_error_translators.register(b'IncompatibleRepositories',
4353
lambda err: errors.IncompatibleRepositories(
4354
err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8'), err.error_args[2].decode('utf-8')))
4355
no_context_error_translators.register(b'LockContention',
4356
lambda err: errors.LockContention('(remote lock)'))
4357
no_context_error_translators.register(b'LockFailed',
4358
lambda err: errors.LockFailed(err.error_args[0].decode('utf-8'), err.error_args[1].decode('utf-8')))
4359
no_context_error_translators.register(b'TipChangeRejected',
4360
lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4361
no_context_error_translators.register(b'UnstackableBranchFormat',
4362
lambda err: branch.UnstackableBranchFormat(*err.error_args))
4363
no_context_error_translators.register(b'UnstackableRepositoryFormat',
4364
lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4365
no_context_error_translators.register(b'FileExists',
4366
lambda err: errors.FileExists(err.error_args[0].decode('utf-8')))
4367
no_context_error_translators.register(b'DirectoryNotEmpty',
4368
lambda err: errors.DirectoryNotEmpty(err.error_args[0].decode('utf-8')))
4369
no_context_error_translators.register(b'UnknownFormat',
4370
lambda err: errors.UnknownFormatError(
4371
err.error_args[0].decode('ascii'), err.error_args[0].decode('ascii')))
4373
def _translate_short_readv_error(err):
4374
args = err.error_args
4375
return errors.ShortReadvError(
4376
args[0].decode('utf-8'),
4377
int(args[1].decode('ascii')), int(args[2].decode('ascii')),
4378
int(args[3].decode('ascii')))
4380
no_context_error_translators.register(b'ShortReadvError',
4381
_translate_short_readv_error)
4383
def _translate_unicode_error(err):
4384
encoding = err.error_args[0].decode('ascii')
4385
val = err.error_args[1].decode('utf-8')
4386
start = int(err.error_args[2].decode('ascii'))
4387
end = int(err.error_args[3].decode('ascii'))
4388
reason = err.error_args[4].decode('utf-8')
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
4389
2937
if val.startswith('u:'):
4390
2938
val = val[2:].decode('utf-8')
4391
2939
elif val.startswith('s:'):